home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir37 / ms_sh23s.zip / SRC / SH3.C < prev    next >
C/C++ Source or Header  |  1994-08-26  |  104KB  |  4,751 lines

  1. /*
  2.  * MS-DOS SHELL - Parse Tree Executor
  3.  *
  4.  * MS-DOS SHELL - Copyright (c) 1990,4 Data Logic Limited and Charles Forsyth
  5.  *
  6.  * This code is based on (in part) the shell program written by Charles
  7.  * Forsyth and is subject to the following copyright restrictions:
  8.  *
  9.  * 1.  Redistribution and use in source and binary forms are permitted
  10.  *     provided that the above copyright notice is duplicated in the
  11.  *     source form and the copyright notice in file sh6.c is displayed
  12.  *     on entry to the program.
  13.  *
  14.  * 2.  The sources (or parts thereof) or objects generated from the sources
  15.  *     (or parts of sources) cannot be sold under any circumstances.
  16.  *
  17.  * When parts of the original 2.1 shell were replaced by the Lexical
  18.  * Analsyer written by Simon J. Gerraty (for his Public Domain Korn Shell,
  19.  * which is also based on Charles Forsyth original idea), a number of changes
  20.  * were made to reflect the changes Simon made to the Parse output tree.  Some
  21.  * parts of this code in this module are based on the algorithms/ideas that
  22.  * he incorporated into his shell, in particular the TCASE and TTIME
  23.  * functionality in ExecuteParseTree, and the PrintAList function.
  24.  *
  25.  *    $Header: /usr/users/istewart/shell/sh2.3/Release/RCS/sh3.c,v 2.17 1994/08/25 20:49:11 istewart Exp $
  26.  *
  27.  *    $Log: sh3.c,v $
  28.  *    Revision 2.17  1994/08/25  20:49:11  istewart
  29.  *    MS Shell 2.3 Release
  30.  *
  31.  *    Revision 2.16  1994/02/23  09:23:38  istewart
  32.  *    Beta 233 updates
  33.  *
  34.  *    Revision 2.15  1994/02/01  10:25:20  istewart
  35.  *    Release 2.3 Beta 2, including first NT port
  36.  *
  37.  *    Revision 2.14  1994/01/20  14:51:43  istewart
  38.  *    Release 2.3 Beta 1
  39.  *
  40.  *    Revision 2.13  1994/01/11  17:55:25  istewart
  41.  *    Release 2.3 Beta 0 patches
  42.  *
  43.  *    Revision 2.12  1993/11/09  10:39:49  istewart
  44.  *    Beta 226 checking
  45.  *
  46.  *    Revision 2.11  1993/08/25  16:03:57  istewart
  47.  *    Beta 225 - see Notes file
  48.  *
  49.  *    Revision 2.10  1993/07/02  10:21:35  istewart
  50.  *    224 Beta fixes
  51.  *
  52.  *    Revision 2.9  1993/06/14  11:00:12  istewart
  53.  *    More changes for 223 beta
  54.  *
  55.  *    Revision 2.8  1993/06/02  09:52:35  istewart
  56.  *    Beta 223 Updates - see Notes file
  57.  *
  58.  *    Revision 2.7  1993/02/16  16:03:15  istewart
  59.  *    Beta 2.22 Release
  60.  *
  61.  *    Revision 2.6  1993/01/26  18:35:09  istewart
  62.  *    Release 2.2 beta 0
  63.  *
  64.  *    Revision 2.5  1992/12/14  10:54:56  istewart
  65.  *    BETA 215 Fixes and 2.1 Release
  66.  *
  67.  *    Revision 2.4  1992/11/06  10:03:44  istewart
  68.  *    214 Beta test updates
  69.  *
  70.  *    Revision 2.3  1992/09/03  18:54:45  istewart
  71.  *    Beta 213 Updates
  72.  *
  73.  *    Revision 2.2  1992/07/16  14:33:34  istewart
  74.  *    Beta 212 Baseline
  75.  *
  76.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  77.  *    211 Beta updates
  78.  *
  79.  *    Revision 2.0  1992/05/07  20:31:39  Ian_Stewartson
  80.  *    MS-Shell 2.0 Baseline release
  81.  *
  82.  */
  83.  
  84. #include <sys/types.h>
  85. #include <sys/stat.h>
  86. #include <stdio.h>
  87. #include <signal.h>
  88. #include <errno.h>
  89. #include <setjmp.h>
  90. #include <ctype.h>
  91. #include <string.h>
  92. #include <unistd.h>
  93. #include <stdlib.h>
  94. #include <fcntl.h>
  95. #include <limits.h>
  96. #include <dirent.h>
  97. #include <ctype.h>
  98. #include <time.h>
  99. #include "sh.h"
  100.  
  101. /*
  102.  * Save struct for Parameters ($1, $2 etc)
  103.  */
  104.  
  105. typedef struct SaveParameters {
  106.     char    **Array;        /* The parameters        */
  107.     int        Count;            /* Number of them        */
  108. } SaveParameters;
  109.  
  110. /* static Function and string declarations */
  111.  
  112. static int F_LOCAL    ForkAndExecute (C_Op *, int, int, int, char **, char **,
  113.                     char **);
  114. static bool F_LOCAL    SetUpIOHandlers (IO_Actions *, int, int);
  115. static bool F_LOCAL    WriteToExtendedFile (FILE *, char *);
  116. static void F_LOCAL    EchoCurrentCommand (char **);
  117. static int F_LOCAL    ExecuteProgram (char *, char **, char **, int);
  118. static bool F_LOCAL    CheckParameterLength (char **);
  119. static void F_LOCAL    SaveNumericParameters (char **, SaveParameters *);
  120. static void F_LOCAL    RestoreTheParameters (SaveParameters *);
  121. static bool F_LOCAL    ExecuteFunction (char **, int *, bool);
  122. static void F_LOCAL    PrintLoadError (char *);
  123. static void F_LOCAL    TrackAllCommands (char *, char *);
  124. static int F_LOCAL    ExtensionType (char *);
  125. static char * F_LOCAL    FindFileAndExtension (char *, char *, char **);
  126. static bool F_LOCAL    GetApplicationType (char *);
  127. static bool F_LOCAL    BadApplication (char *);
  128. static bool F_LOCAL    SetUpCLI (char *, Word_B **);
  129.  
  130. #ifdef OS_SWAPPING
  131. static bool F_LOCAL    Get_EMS_Driver (void);
  132. static bool F_LOCAL    Get_XMS_Driver (void);
  133. static bool F_LOCAL    EMS_error (char *, int);
  134. static int F_LOCAL    EMS_Close (void);
  135. static bool F_LOCAL    XMS_error (char *, int);
  136. static int F_LOCAL    XMS_Close (void);
  137. static int F_LOCAL    SwapToDiskError (int, char *);
  138. static int F_LOCAL    SwapToMemory (int, char **);
  139. static int F_LOCAL    SpawnProcess (char **);
  140. static int F_LOCAL    CheckForExecOnly (char *, int, int);
  141. #endif
  142.  
  143. static char * F_LOCAL    ConvertErrorNumber (void);
  144. static int F_LOCAL    BuildCommandLine (char *, char **, char **, int);
  145. static int F_LOCAL    StartTheProcess (char *, char **, char **, int);
  146. static int F_LOCAL    SetCommandReturnStatus (int);
  147. static char ** F_LOCAL    FindNumberOfValues (char **, int *);
  148. static int F_LOCAL    ExecuteScriptFile (char *, char **, char **, int, bool);
  149. #if (OS_TYPE != OS_UNIX)
  150. static int F_LOCAL    ExecuteWindows (char *, char **, char **, int);
  151. #endif
  152. static int F_LOCAL    ExecuteSpecialProcessor (char *, char **, char **, int,
  153.                          Word_B *);
  154. static int F_LOCAL    EnvironExecute (char **, int);
  155. static int F_LOCAL     LocalExecve (char **, char **, int);
  156. static unsigned int F_LOCAL CheckForCommonOptions (LineFields *, int);
  157. static char ** F_LOCAL    ProcessSpaceInParameters (char **);
  158. static int F_LOCAL    CountDoubleQuotes (char *);
  159. static void        BuildEnvironmentEntry (const void *, VISIT, int);
  160. static char ** F_LOCAL    BuildCommandEnvironment (void);
  161.  
  162. #if (OS_TYPE != OS_DOS)
  163. static void F_LOCAL    PrintPidStarted (void);
  164.  
  165. #  if (OS_TYPE == OS_OS2)
  166. static char        *InsertCharacterAtStart (char *);
  167. static int F_LOCAL    OS_DosExecProgram (int, char *, char **, char **);
  168. #  elif (OS_TYPE == OS_NT)
  169. static int F_LOCAL    OS_DosExecProgram (int, char *, char **, char **);
  170. #  endif
  171. #else
  172. #  define PrintPidStarted()
  173. #  define InsertCharacterAtStart(a)
  174. #endif
  175.  
  176. #if (OS_TYPE == OS_OS2)
  177. static int F_LOCAL    StartTheSession (STARTDATA *, char *, char **,
  178.                      char **, int);
  179. #endif
  180.  
  181. static char        *AE2big = "arg/env list too big";
  182.             /* Extended Command line processing file name    */
  183. static char        *Extend_file = (char *)NULL;
  184. static char        *DoubleQuotes = "\"";
  185. static char        *WildCards = "*?[\"'";
  186. static unsigned long    ApplicationType;
  187.  
  188. #if (OS_TYPE == OS_DOS)
  189. static char        *LIT_STARTWINP = "STARTWINP";
  190. #endif
  191.  
  192. /*
  193.  * Global variables for TWALK to build command environment
  194.  */
  195.  
  196. static Word_B    *BCE_WordList;
  197. static int    BCE_Length;
  198.  
  199. /*
  200.  * List of Executable (shell script, .exe, .com) extensions and list
  201.  * including functions.
  202.  */
  203.  
  204. static char    **ExecutableList = (char **)NULL;
  205. static char    **ExecuteFunctionList = (char **)NULL;
  206.  
  207. /* Swapping messages */
  208.  
  209. #ifdef OS_SWAPPING
  210. static char    *NoSwapFiles = "No Swap files";
  211. static char    *MS_emsg = "Warning: %s Error (%x)";
  212. static char    *MS_Space = "Warning: %s out of space";
  213. static char    *SwapFailed = "%s swap failed (%x)";
  214. static char    *Swap_File = (char *)NULL;    /* Swap file    */
  215. #endif
  216.  
  217. /*
  218.  * OS2 load error mode
  219.  */
  220.  
  221. #if (OS_TYPE == OS_OS2) 
  222. static char        FailName[FFNAME_MAX];
  223. #endif
  224.  
  225. #if (OS_TYPE != OS_DOS) 
  226. static OSCALL_RET    OS_DosExecPgmReturnCode;
  227. #endif
  228.  
  229. /*
  230.  * Program started info
  231.  */
  232.  
  233. #if (OS_TYPE != OS_DOS) 
  234. struct PidInfo {
  235.     bool    Valid;
  236.     int        JobNo;
  237.     int        PidNo;
  238. } PidInfo;
  239. #endif
  240.  
  241. /*
  242.  * Common fields in EXTENDED_LINE file
  243.  */
  244.  
  245. #define COMMON_FIELD_COUNT    ARRAY_SIZE (CommonFields)
  246.  
  247. static struct CommonFields {
  248.     char        *Name;
  249.     unsigned int    Flag;
  250. } CommonFields [] = {
  251.     { "switch",        EP_CONVERT },
  252.     { LIT_export,    EP_EXPORT },
  253.     { "noswap",        EP_NOSWAP },
  254.     { "noexpand",    EP_NOEXPAND },
  255.     { "noquote",    EP_NOQUOTE },
  256.     { "ignoretype",    EP_IGNTYPE },
  257.     { "pipetty",    EP_PSEUDOTTY },
  258.     { "quotewild",    EP_QUOTEWILD }
  259. };
  260.  
  261. /*
  262.  * execute tree recursively
  263.  */
  264.  
  265. int ExecuteParseTree (C_Op    *t,
  266.               int    StandardIN,
  267.               int    StandardOUT,
  268.               int    Actions)
  269. {
  270.     int            Count;
  271.     int            LocalPipeFP;    /* Pipe handlers        */
  272.  
  273. #if (OS_TYPE != OS_DOS) 
  274.     int            ReadPipeFP;
  275.     int            WritePipeFP;
  276. #  if OS_TYPE == OS_NT
  277.     extern int        _open_osfhandle (long, int);
  278.     HANDLE        ReadPipeH;
  279.     HANDLE        WritePipeH;
  280. #  endif
  281. #endif
  282.  
  283.     char        *cp;
  284.     char        **AList;    /* Argument list        */
  285.     Break_C        BreakContinue;
  286.                     /* Save longjmp returns        */
  287.     Break_C        *S_RList = Return_List;
  288.     Break_C        *S_BList = Break_List;
  289.     Break_C        *S_SList = SShell_List;
  290.  
  291.     GetoptsIndex    GetoptsSave;
  292.     int            Local_depth;    /* Save local values        */
  293.     int            Local_MemoryAreaLevel;
  294.     int            RetVal = 0;    /* Return value            */
  295.     char        *InputBuffer;    /* Select input Buffer        */
  296.     char        *EndIB;        /* End of buffer        */
  297.     char        *LastWord = null;
  298.  
  299. /* End of tree ? */
  300.  
  301.     if (t == (C_Op *)NULL)
  302.     return SetCommandReturnStatus (0);
  303.  
  304.     DPRINT (1, ("ExecuteParseTree: t->type = %d, Depth = %d",
  305.             t->type, Execute_stack_depth));
  306.  
  307. /* Save original and Increment execute function recursive level */
  308.  
  309.     Local_depth = Execute_stack_depth++;
  310.  
  311. /* Save original and increment area number */
  312.  
  313.     Local_MemoryAreaLevel = MemoryAreaLevel++;
  314.  
  315. /* Switch on tree node type */
  316.  
  317.     switch (t->type)
  318.     {
  319.     case TFUNC:            /* name () { list; }    */
  320.         RetVal = SaveFunction (t) ? 0 : 1;
  321.         SetCommandReturnStatus (RetVal);
  322.         break;
  323.  
  324. /* In the case of a () command string, we need to save and restore the
  325.  * current environment, directory and traps (can't think of anything else).
  326.  * For any other, we just restore the current directory.  Also, we don't
  327.  * want changes in the Variable list header saved for SubShells, because
  328.  * we are effectively back at execute depth zero.
  329.  */
  330.     case TPAREN:            /* ()            */
  331.         if ((RetVal = CreateGlobalVariableList (FLAGS_NONE)) == -1)
  332.         break;
  333.  
  334. /* Save Getopts pointers */
  335.  
  336.         GetGetoptsValues (&GetoptsSave);
  337.  
  338.  
  339.         if (setjmp (BreakContinue.CurrentReturnPoint) == 0)
  340.         {
  341.         Return_List = (Break_C *)NULL;
  342.         Break_List  = (Break_C *)NULL;
  343.         BreakContinue.NextExitLevel  = SShell_List;
  344.         SShell_List = &BreakContinue;
  345.         RetVal = ForkAndExecute (t, StandardIN, StandardOUT, Actions,
  346.                      NOWORDS, NOWORDS, &LastWord);
  347.         }
  348.  
  349. /* Restore the original environment */
  350.  
  351.         else
  352.         RetVal = (int)GetVariableAsNumeric (StatusVariable);
  353.  
  354.         SaveGetoptsValues (GetoptsSave.Index, GetoptsSave.SubIndex);
  355.         Return_List    = S_RList;
  356.         Break_List    = S_BList;
  357.         SShell_List    = S_SList;
  358.         RestoreEnvironment (RetVal, Local_depth);
  359.         break;
  360.  
  361. /* After a normal command, we need to restore the original directory.  Note
  362.  * that a cd will have updated the variable $~, so no problem
  363.  */
  364.  
  365.     case TCOM:            /* A command process    */
  366.     {
  367.         ExeMode     SaveValues;
  368.         char    **SetVlist;    /* Set variable list        */
  369.  
  370.         SetVlist = ExpandWordList (t->vars, EXPAND_TILDE, (ExeMode *)NULL);
  371.         AList = ExpandWordList (t->args, EXPAND_SPLITIFS | EXPAND_GLOBBING |
  372.                     EXPAND_TILDE, &SaveValues);
  373.  
  374.         ExecProcessingMode = SaveValues;
  375.  
  376. #if (OS_TYPE != OS_DOS) 
  377.         PidInfo.Valid = FALSE;
  378. #endif
  379.  
  380.         RetVal = ForkAndExecute (t, StandardIN, StandardOUT, Actions,
  381.                      AList, SetVlist, &LastWord);
  382.  
  383.         PrintPidStarted ();
  384.         RestoreEnvironment (RetVal, Local_depth);
  385.  
  386. /* Save last word if appropriate */
  387.  
  388.         if (!(DisabledVariables & DISABLE_LASTWORD))
  389.         {
  390.         SetVariableFromString (LastWordVariable, LastWord);
  391.         SetVariableStatus (LastWordVariable, STATUS_EXPORT);
  392.         }
  393.         break;
  394.     }
  395.  
  396.     case TTIME:            /* Time a command process    */
  397.     {
  398.         clock_t    stime;
  399.         clock_t    etime;
  400.         clock_t    dif;
  401.  
  402.         stime = clock ();
  403.         RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT, 0);
  404.         etime = clock ();
  405.  
  406.         feputc (CHAR_NEW_LINE);
  407.  
  408.         if ((dif = (etime - stime) / (60L * (clock_t)CLOCKS_PER_SEC)))
  409.         fprintf (stderr, "%ldm ", dif);
  410.  
  411.         dif = (etime - stime) % (60L * (clock_t)CLOCKS_PER_SEC);
  412.  
  413.         fprintf (stderr, "%ld.%.3lds real\n",
  414.              dif / (clock_t)CLOCKS_PER_SEC,
  415.              dif % (clock_t)CLOCKS_PER_SEC);
  416.  
  417.         break;
  418.     }
  419.  
  420.     case TPIPE:            /* Pipe processing        */
  421.  
  422.         Actions |= EXEC_PIPE_IN;
  423.  
  424. #if (OS_TYPE == OS_UNIX) 
  425.         fputs ("UNIX: Pipes not implemented\n", stderr);
  426.         RetVal = -1;
  427.  
  428. #else
  429. #  if (OS_TYPE != OS_DOS) 
  430. /* Do we want to use real pipes under OS2? */
  431.  
  432.         if (ShellGlobalFlags & FLAGS_REALPIPES)
  433.         {
  434. #    if (OS_TYPE == OS_OS2)
  435. #      if (OS_SIZE == OS_32)
  436.         if (DosCreatePipe ((PHFILE) &ReadPipeFP,
  437.                    (PHFILE) &WritePipeFP, 4096))
  438.             break;
  439. #      else
  440.         if (DosMakePipe ((PHFILE) &ReadPipeFP,
  441.                  (PHFILE) &WritePipeFP, 0))
  442.             break;
  443. #      endif
  444.  
  445. /* Remap the IO handler */
  446.  
  447.         ReadPipeFP = ReMapIOHandler (ReadPipeFP);
  448.         WritePipeFP = ReMapIOHandler (WritePipeFP);
  449.         DosSetFHandState (ReadPipeFP, OPEN_FLAGS_NOINHERIT);
  450.         DosSetFHandState (WritePipeFP, OPEN_FLAGS_NOINHERIT);
  451.  
  452. #    elif (OS_TYPE == OS_NT)
  453.         if (!CreatePipe (&ReadPipeH, &WritePipeH, 0, 0))
  454.             break;
  455.         
  456. /* Remap the IO handler */
  457.  
  458.         ReadPipeFP = _open_osfhandle ((long)ReadPipeH, _O_RDONLY);
  459.         WritePipeFP = _open_osfhandle ((long)WritePipeH, _O_APPEND);
  460.         ReadPipeFP = ReMapIOHandler (ReadPipeFP);
  461.         WritePipeFP = ReMapIOHandler (WritePipeFP);
  462. #    endif
  463.  
  464.  
  465. /* Is this a foreground thingy? */
  466.  
  467.         if (!(Actions & (EXEC_SPAWN_NOWAIT | EXEC_SPAWN_IGNOREWAIT)))
  468.         {
  469.             int        WaitPid;
  470.  
  471.             WaitPid = ExecuteParseTree (t->left, StandardIN,
  472.                         WritePipeFP,
  473.                             EXEC_SPAWN_IGNOREWAIT |
  474.                             (Actions & (EXEC_PIPE_IN |
  475.                                 EXEC_PIPE_SUBS)));
  476.             S_close (WritePipeFP, TRUE);
  477.  
  478.             RetVal = ExecuteParseTree (t->right, ReadPipeFP,
  479.                            StandardOUT,
  480.                            Actions | EXEC_PIPE_SUBS);
  481.             S_close (ReadPipeFP, TRUE);
  482.             cwait (&WaitPid, WaitPid, WAIT_GRANDCHILD);
  483.         }
  484.  
  485. /* Background processing */
  486.  
  487.         else
  488.         {
  489.             ExecuteParseTree (t->left, StandardIN, WritePipeFP,
  490.                       EXEC_SPAWN_IGNOREWAIT |
  491.                       (Actions & (EXEC_PIPE_IN |
  492.                           EXEC_PIPE_SUBS)));
  493.  
  494.             S_close (WritePipeFP, TRUE);
  495.  
  496.             RetVal = ExecuteParseTree (t->right, ReadPipeFP,
  497.                            StandardOUT,
  498.                            Actions | EXEC_PIPE_SUBS);
  499.             S_close (ReadPipeFP, TRUE);
  500.         }
  501.  
  502.         break;
  503.         }
  504. #  endif
  505.  
  506. /* MSDOS or OS/2 without real pipes - use files.  Safer */
  507.  
  508.         if ((RetVal = OpenAPipe ()) < 0)
  509.         break;
  510.  
  511. /* Create pipe, execute command, reset pipe, execute the other side, close
  512.  * the pipe and fini
  513.  */
  514.  
  515.         LocalPipeFP = ReMapIOHandler (RetVal);
  516.         ExecuteParseTree (t->left, StandardIN, LocalPipeFP,
  517.                   (Actions & (EXEC_PIPE_IN | EXEC_PIPE_SUBS)));
  518.  
  519. /* Close the Input to release the file descriptor */
  520.  
  521.         CloseThePipe (StandardIN);
  522.  
  523.         lseek (LocalPipeFP, 0L, SEEK_SET);
  524.         RetVal = ExecuteParseTree (t->right, LocalPipeFP, StandardOUT,
  525.                        Actions | EXEC_PIPE_SUBS);
  526.         CloseThePipe (LocalPipeFP);
  527. #endif
  528.         break;
  529.  
  530.     case TLIST:            /* Entries in a for statement    */
  531.         while (t->type == TLIST)
  532.         {
  533.         ExecuteParseTree (t->left, StandardIN, StandardOUT, 0);
  534.         t = t->right;
  535.         }
  536.  
  537.         RetVal = ExecuteParseTree (t, StandardIN, StandardOUT, 0);
  538.         break;
  539.  
  540.     case TCOPROCESS:        /* Co processes            */
  541.         if (!FL_TEST (FLAG_WARNING))
  542.         PrintWarningMessage ("sh: co-processes not supported");
  543.  
  544.         SetCommandReturnStatus (RetVal = -1);
  545.         break;
  546.  
  547.     case TASYNC:            /* Async - not supported    */
  548. #if (OS_TYPE == OS_DOS) 
  549.         if (!FL_TEST (FLAG_WARNING))
  550.         PrintWarningMessage ("sh: Async commands not supported");
  551.  
  552.         SetCommandReturnStatus (RetVal = -1);
  553. #else
  554.         RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
  555.                        EXEC_SPAWN_NOWAIT);
  556. #endif
  557.         break;
  558.  
  559.     case TOR:            /* || and &&            */
  560.     case TAND:
  561.         RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT, 0);
  562.  
  563.         if ((t->right != (C_Op *)NULL) &&
  564.         ((RetVal == 0) == (t->type == TAND)))
  565.         RetVal = ExecuteParseTree (t->right, StandardIN, StandardOUT,
  566.                        0);
  567.  
  568.         break;
  569.  
  570.  
  571. /* for x do...done and for x in y do...done - find the start of the variables
  572.  * count the number.
  573.  */
  574.     case TFOR:
  575.     case TSELECT:
  576.         AList = FindNumberOfValues (ExpandWordList (t->vars,
  577.                             EXPAND_SPLITIFS |
  578.                                 EXPAND_GLOBBING |
  579.                                 EXPAND_TILDE,
  580.                             (ExeMode *)NULL),
  581.                             &Count);
  582.  
  583.  
  584. /* Set up a long jump return point before executing the for function so that
  585.  * the continue statement is executed, ie we reprocessor the for condition.
  586.  */
  587.  
  588.         while ((RetVal = setjmp (BreakContinue.CurrentReturnPoint)))
  589.         {
  590.  
  591. /* Restore the current stack level and clear out any I/O */
  592.  
  593.         RestoreEnvironment (0, Local_depth + 1);
  594.         Return_List = S_RList;
  595.         SShell_List = S_SList;
  596.  
  597. /* If this is a break - clear the variable and terminate the while loop and
  598.  * switch statement
  599.  */
  600.  
  601.         if (RetVal == BC_BREAK)
  602.             break;
  603.         }
  604.  
  605.         if (RetVal == BC_BREAK)
  606.         break;
  607.  
  608. /* Process the next entry - Add to the break/continue chain */
  609.  
  610.         BreakContinue.NextExitLevel = Break_List;
  611.         Break_List = &BreakContinue;
  612.  
  613. /* Execute the command tree */
  614.  
  615.         if (t->type == TFOR)
  616.         {
  617.         while (Count--)
  618.         {
  619.             SetVariableFromString (t->str, *AList++);
  620.             RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
  621.                            0);
  622.         }
  623.         }
  624.  
  625. /* Select option */
  626.  
  627.         else if (!Count)
  628.         /* SKIP */;
  629.  
  630. /* Get some memory for the select input buffer */
  631.  
  632.         else if ((InputBuffer = AllocateMemoryCell (LINE_MAX))
  633.             == (char *)NULL)
  634.         {
  635.         ShellErrorMessage (Outofmemory1);
  636.         RetVal = -1;
  637.         }
  638.  
  639. /* Process the select command */
  640.  
  641.         else
  642.         {
  643.         bool    OutputList = TRUE;
  644.  
  645.         EndIB = &InputBuffer[LINE_MAX - 2];
  646.  
  647.         while (TRUE)
  648.         {
  649.             int        ReadCount;    /* Local counter    */
  650.             int        OnlyDigits;    /* Only digits in string*/
  651.  
  652. /* Output list of words */
  653.  
  654.             if (OutputList)
  655.             {
  656.                 PrintAList (Count, AList);
  657.                 OutputList = FALSE;
  658.             }
  659.  
  660. /* Output prompt */
  661.  
  662.             OutputUserPrompt (PS3);
  663.             OnlyDigits = 1;
  664.  
  665. /* Read in until end of line, file or a field separator is detected */
  666.  
  667.             for (cp = InputBuffer; (cp < EndIB); cp++)
  668.             {
  669.             if (((ReadCount = read (STDIN_FILENO, cp, 1)) != 1) ||
  670.                 (*cp == CHAR_NEW_LINE))
  671.             {
  672.                 break;
  673.             }
  674.  
  675.             OnlyDigits = OnlyDigits && isdigit (*cp);
  676.             }
  677.  
  678.             *cp = 0;
  679.  
  680. /* Check for end of file */
  681.  
  682.             if (ReadCount != 1)
  683.             break;
  684.  
  685. /* Check for empty line */
  686.  
  687.             if (!strlen (InputBuffer))
  688.             {
  689.             OutputList = TRUE;
  690.             continue;
  691.             }
  692.  
  693.             SetVariableFromString (LIT_REPLY, InputBuffer);
  694.  
  695. /* Check that OnlyDigits is a valid number in the select range */
  696.  
  697.             if (OnlyDigits &&
  698.             ((OnlyDigits = atoi (InputBuffer)) > 0) &&
  699.             (OnlyDigits <= Count))
  700.             SetVariableFromString (t->str, AList[OnlyDigits - 1]);
  701.  
  702.             else
  703.             SetVariableFromString (t->str, null);
  704.  
  705.             RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
  706.                            0);
  707.         }
  708.         }
  709.  
  710. /* Remove this tree from the break list */
  711.  
  712.         Break_List = S_BList;
  713.         break;
  714.  
  715. /* While and Until function.  Similar to the For function.  Set up a
  716.  * long jump return point before executing the while function so that
  717.  * the continue statement is executed OK.
  718.  */
  719.  
  720.     case TWHILE:            /* WHILE and UNTIL functions    */
  721.     case TUNTIL:
  722.         while ((RetVal = setjmp (BreakContinue.CurrentReturnPoint)))
  723.         {
  724.  
  725. /* Restore the current stack level and clear out any I/O */
  726.  
  727.         RestoreEnvironment (0, Local_depth + 1);
  728.         Return_List = S_RList;
  729.         SShell_List = S_SList;
  730.  
  731. /* If this is a break, terminate the while and switch statements */
  732.  
  733.         if (RetVal == BC_BREAK)
  734.             break;
  735.         }
  736.  
  737.         if (RetVal == BC_BREAK)
  738.         break;
  739.  
  740. /* Set up links */
  741.  
  742.         BreakContinue.NextExitLevel = Break_List;
  743.         Break_List = &BreakContinue;
  744.  
  745.         while ((ExecuteParseTree (t->left, StandardIN, StandardOUT, 0) == 0)
  746.                 == (t->type == TWHILE))
  747.         RetVal = ExecuteParseTree (t->right, StandardIN, StandardOUT,
  748.                        0);
  749.  
  750.         Break_List = S_BList;
  751.         break;
  752.  
  753.     case TIF:            /* IF and ELSE IF functions    */
  754.     case TELIF:
  755.         if (t->right != (C_Op *)NULL)
  756.         RetVal = ExecuteParseTree (!ExecuteParseTree (t->left,
  757.                                   StandardIN,
  758.                                   StandardOUT, 0)
  759.                         ? t->right->left : t->right->right,
  760.                         StandardIN, StandardOUT, 0);
  761.  
  762.         break;
  763.  
  764.     case TCASE:            /* CASE function        */
  765.     {
  766.         C_Op    *ts = t->left;
  767.  
  768.         cp = ExpandAString (t->str, 0);
  769.  
  770.         while ((ts != (C_Op *)NULL) && (ts->type == TPAT))
  771.         {
  772.         for (AList = ts->vars; *AList != NOWORD; AList++)
  773.         {
  774.             if ((EndIB = ExpandAString (*AList, EXPAND_PATTERN)) &&
  775.             GeneralPatternMatch (cp, (unsigned char *)EndIB, FALSE,
  776.                          (char **)NULL, GM_ALL))
  777.             goto Found;
  778.         }
  779.  
  780.         ts = ts->right;
  781.         }
  782.  
  783.         break;
  784.  
  785. Found:
  786.         RetVal = ExecuteParseTree (ts->left, StandardIN, StandardOUT, 0);
  787.         break;
  788.     }
  789.  
  790.     case TBRACE:            /* {} statement            */
  791.         if ((RetVal >= 0) && (t->left != (C_Op *)NULL))
  792.         RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
  793.                        (Actions & EXEC_FUNCTION));
  794.  
  795.         break;
  796.     }
  797.  
  798. /* Processing Completed - Restore environment */
  799.  
  800.     Execute_stack_depth = Local_depth;
  801.  
  802. /* Remove unwanted malloced space */
  803.  
  804.     ReleaseMemoryArea (MemoryAreaLevel);
  805.     MemoryAreaLevel = Local_MemoryAreaLevel;
  806.  
  807. /* Check for traps */
  808.  
  809.     if (t->type == TCOM)
  810.     {
  811.     RunTrapCommand (-1);        /* Debug trap            */
  812.  
  813.     if (RetVal)
  814.         RunTrapCommand (-2);    /* Err trap            */
  815.     }
  816.  
  817. /* Interrupt traps */
  818.  
  819.     if ((Count = InterruptTrapPending) != 0)
  820.     {
  821.     InterruptTrapPending = 0;
  822.     RunTrapCommand (Count);
  823.     }
  824.  
  825. /* Check for interrupts */
  826.  
  827.     if (InteractiveFlag && IS_TTY (0) && SW_intr)
  828.     {
  829.     CloseAllHandlers ();
  830.     TerminateCurrentEnvironment (TERMINATE_COMMAND);
  831.     }
  832.  
  833.     return RetVal;
  834. }
  835.  
  836. /*
  837.  * Restore the original directory
  838.  */
  839.  
  840. void RestoreCurrentDirectory (char *path)
  841. {
  842.     SetCurrentDrive (GetDriveNumber (*path));
  843.  
  844.     if (!S_chdir (&path[2]))
  845.     {
  846.     if (!FL_TEST (FLAG_WARNING))
  847.         feputs ("Warning: current directory reset to /\n");
  848.  
  849.     S_chdir (DirectorySeparator);
  850.     GetCurrentDirectoryPath ();
  851.     }
  852. }
  853.  
  854. /*
  855.  * Ok - execute the program, resetting any I/O required
  856.  */
  857.  
  858. static int F_LOCAL ForkAndExecute (C_Op    *t,
  859.                    int    StandardIN,
  860.                    int    StandardOUT,
  861.                    int    ForkAction,
  862.                    char    **AList,
  863.                    char    **VList,
  864.                    char    **LastWord)
  865. {
  866.     int            RetVal = -1;    /* Return value            */
  867.     int            (*shcom)(int, char **) = (int (*)(int, char **))NULL;
  868.     char        *cp;
  869.     char        **alp;
  870.     IO_Actions        **iopp = t->ioact;
  871.     int            builtin = 0;    /* Builtin function        */
  872.     bool        CGVLCalled = FALSE;
  873.     bool        InternalExec = FALSE;
  874.     int            Index;
  875.  
  876.     if (t->type == TCOM)
  877.     {
  878.     cp = *AList;
  879.  
  880. /* strip all initial assignments not correct wrt PATH=yyy command  etc */
  881.  
  882.     if (FL_TEST (FLAG_PRINT_EXECUTE) ||
  883.        ((CurrentFunction != (FunctionList *)NULL) &&
  884.         CurrentFunction->Traced))
  885.         EchoCurrentCommand (cp != NOWORD ? AList : VList);
  886.  
  887. /* Is it only an assignement? */
  888.  
  889.     if ((cp == NOWORD) && (t->ioact == (IO_Actions **)NULL))
  890.     {
  891.         while (((cp = *(VList++)) != NOWORD) &&
  892.            AssignVariableFromString (cp, &Index))
  893.         continue;
  894.  
  895. #if (OS_TYPE != OS_DOS) 
  896.         ExitWithJobsActive = FALSE;
  897. #endif
  898.  
  899. /* Get the status variable if expand changed it */
  900.  
  901.         return (int)GetVariableAsNumeric (StatusVariable);
  902.     }
  903.  
  904. /* Check for built in commands */
  905.  
  906.     else if (cp != NOWORD)
  907.     {
  908.         shcom = IsCommandBuiltIn (cp, &builtin);
  909.         InternalExec = C2bool ((strcmp (cp, LIT_exec)) == 0);
  910.  
  911. /*
  912.  * Reset the ExitWithJobsActive flag to enable the 'jobs active' on exit
  913.  * message
  914.  */
  915.  
  916. #if (OS_TYPE != OS_DOS) 
  917.         if (strcmp (cp, LIT_exit))
  918.         ExitWithJobsActive = FALSE;
  919. #endif
  920.     }
  921.  
  922. #if (OS_TYPE != OS_DOS) 
  923.     else
  924.         ExitWithJobsActive = FALSE;
  925. #endif
  926.     }
  927.  
  928. #if (OS_TYPE != OS_DOS) 
  929.     else
  930.     ExitWithJobsActive = FALSE;
  931. #endif
  932.  
  933. /*
  934.  * UNIX fork simulation?
  935.  */
  936.  
  937. /* If there is a command to execute or we are exec'ing and this is not a
  938.  * TPAREN, save the current environment
  939.  */
  940.  
  941.     if (t->type != TPAREN)
  942.     {
  943.     if (((*VList != NOWORD) &&
  944.          ((builtin & BLT_SKIPENVIR) != BLT_SKIPENVIR)) ||
  945.         (ForkAction & EXEC_WITHOUT_FORK))
  946.     {
  947.         if (CreateGlobalVariableList (FLAGS_FUNCTION) == -1)
  948.         return -1;
  949.  
  950.         CGVLCalled = TRUE;
  951.     }
  952.  
  953. /* Set up any variables.  Note there is an assumption that
  954.  * AssignVariableFromString sets the equals sign to 0, hiding the value;
  955.  */
  956.  
  957.     if (shcom == (int (*)(int, char **))NULL)
  958.     {
  959.         while (((cp = *(VList++)) != NOWORD) &&
  960.            AssignVariableFromString (cp, &Index))
  961.         SetVariableArrayStatus (cp, Index,
  962.                     (STATUS_EXPORT | STATUS_LOCAL));
  963.  
  964. /* If the child shells from this process communicate via pipes, set the
  965.  * environment so the child shell will know.  This is a special case for OS/2
  966.  * (and Win NT?) EMACS.
  967.  */
  968.  
  969.        if (ExecProcessingMode.Flags & EP_PSEUDOTTY)
  970.        {
  971.            char    temp[30];
  972.  
  973.            AssignVariableFromString (strcat (strcpy (temp, LIT_AllowTTY),
  974.                          "=true"), &Index);
  975.            SetVariableArrayStatus (temp, Index,
  976.                        (STATUS_EXPORT | STATUS_LOCAL));
  977.        }
  978.     }
  979.     }
  980.  
  981.  
  982. /* If this is the first command in a pipe, set stdin to /dev/null */
  983.  
  984.    if ((ForkAction & (EXEC_PIPE_IN | EXEC_PIPE_SUBS)) == EXEC_PIPE_IN)
  985.    {
  986.     if ((Index = S_open (FALSE, "/dev/null", O_RDONLY)) != 0)
  987.     {
  988.         S_dup2 (Index, 0);
  989.         S_close (Index, TRUE);
  990.     }
  991.    }
  992.  
  993. /* We cannot close the pipe, because once the exec/spawn has taken place
  994.  * the processing of the pipe is not yet complete.
  995.  */
  996.  
  997.     if (StandardIN != NOPIPE)
  998.     {
  999.     S_dup2 (StandardIN, STDIN_FILENO);
  1000.     /*lseek (STDIN_FILENO, 0L, SEEK_SET);*/
  1001.     }
  1002.  
  1003.     if (StandardOUT != NOPIPE)
  1004.     {
  1005.     FlushStreams ();
  1006.     S_dup2 (StandardOUT, STDOUT_FILENO);
  1007.     lseek (STDOUT_FILENO, 0L, SEEK_END);
  1008.     }
  1009.  
  1010. /* Set up any other IO required */
  1011.  
  1012.     FlushStreams ();
  1013.  
  1014.     if (iopp != (IO_Actions **)NULL)
  1015.     {
  1016.     while (*iopp != (IO_Actions *)NULL)
  1017.     {
  1018.         if (SetUpIOHandlers (*(iopp++), StandardIN, StandardOUT))
  1019.         return RetVal;
  1020.     }
  1021.     }
  1022.  
  1023.  
  1024. /* All fids above 10 are autoclosed in the exec file because we have used
  1025.  * the O_NOINHERIT flag.  Note I patched open.obj to pass this flag to the
  1026.  * open function.
  1027.  */
  1028.  
  1029.     if (t->type == TPAREN)
  1030.     return RestoreStandardIO (ExecuteParseTree (t->left, NOPIPE, NOPIPE, 0),
  1031.                   TRUE);
  1032.  
  1033. /* Are we just changing the I/O re-direction for the shell ? */
  1034.  
  1035.     if (*AList == NOWORD)
  1036.     {
  1037.     if ((ForkAction & EXEC_WITHOUT_FORK) == 0)
  1038.         RestoreStandardIO (0, TRUE);
  1039.  
  1040. /* Get the status variable if expand changed it */
  1041.  
  1042.     return (int)GetVariableAsNumeric (StatusVariable);
  1043.     }
  1044.  
  1045. /*
  1046.  * Find the end of the parameters and set up $_ environment variable
  1047.  */
  1048.  
  1049.     alp = AList;
  1050.     while (*alp != NOWORD)
  1051.        alp++;
  1052.  
  1053. /* Move back to last parameter, and save it */
  1054.  
  1055.     *LastWord = StringCopy (*(alp - 1));
  1056.  
  1057. /* No - Check for a function the program.  At this point, we need to put
  1058.  * in some processing for return.
  1059.  */
  1060.  
  1061.     if (!(builtin & BLT_CURRENT) &&
  1062.     ExecuteFunction (AList, &RetVal, CGVLCalled))
  1063.     return RetVal;
  1064.  
  1065. /* Check for another drive or directory in the restricted shell */
  1066.  
  1067.     if ((strpbrk (*AList, ":/\\") != (char *)NULL) &&
  1068.     CheckForRestrictedShell (*AList))
  1069.     return RestoreStandardIO (-1, TRUE);
  1070.  
  1071. /* A little cheat to allow us to use the same code to start OS/2 sessions
  1072.  * as to load and execute a program
  1073.  */
  1074.  
  1075. #if (OS_TYPE == OS_OS2) 
  1076.     SessionControlBlock = (STARTDATA *)NULL;
  1077. #endif
  1078.  
  1079. /* Ok - execute the program */
  1080.  
  1081.     if (!(builtin & BLT_CURRENT))
  1082.     {
  1083.     RetVal = EnvironExecute (AList, ForkAction);
  1084.  
  1085.     if (ExecProcessingMode.Flags != EP_ENVIRON)
  1086.         RetVal = LocalExecve (AList, BuildCommandEnvironment (),
  1087.                   ForkAction);
  1088.     }
  1089.  
  1090. /* If we didn't find it, check for internal command
  1091.  *
  1092.  * Note that the exec command is a special case
  1093.  */
  1094.  
  1095.     if ((builtin & BLT_CURRENT) || ((RetVal == -1) && (errno == ENOENT)))
  1096.     {
  1097.     if (shcom != (int (*)(int, char **))NULL)
  1098.     {
  1099.         if (InternalExec)
  1100.         RetVal = doexec (t);
  1101.  
  1102.         else
  1103.         RetVal = (*shcom)(CountNumberArguments (AList), AList);
  1104.  
  1105.         SetCommandReturnStatus (RetVal);
  1106.     }
  1107.     }
  1108.  
  1109.     if (RetVal == -1)
  1110.     PrintLoadError (*AList);
  1111.  
  1112.     return RestoreStandardIO (RetVal, TRUE);
  1113. }
  1114.  
  1115. /*
  1116.  * Restore Local Environment
  1117.  */
  1118.  
  1119. void RestoreEnvironment (int retval, int stack)
  1120. {
  1121.     Execute_stack_depth = stack;
  1122.     DeleteGlobalVariableList ();
  1123.     RestoreCurrentDirectory (CurrentDirectory->value);
  1124.     RestoreStandardIO (SetCommandReturnStatus (retval), TRUE);
  1125. }
  1126.  
  1127. /*
  1128.  * Set up I/O redirection.  0< 1> are ignored as required within pipelines.
  1129.  */
  1130.  
  1131. static bool F_LOCAL SetUpIOHandlers (IO_Actions    *iop,
  1132.                      int    pipein,
  1133.                      int    pipeout)
  1134. {
  1135.     int        u;
  1136.     char    *cp, *msg;
  1137.  
  1138. /* Check for pipes */
  1139.  
  1140.     if ((pipein != NOPIPE) && (iop->io_unit == STDIN_FILENO))
  1141.     return FALSE;
  1142.  
  1143.     if ((pipeout != NOPIPE) && (iop->io_unit == STDOUT_FILENO))
  1144.     return FALSE;
  1145.  
  1146.     msg = ((iop->io_flag & IOTYPE) == IOWRITE) ? "create" : "open";
  1147.  
  1148.     if ((iop->io_flag & IOTYPE) != IOHERE)
  1149.     cp = ExpandOneStringFirstComponent (iop->io_name,
  1150.                         EXPAND_TILDE | EXPAND_GLOBBING);
  1151.  
  1152. /*
  1153.  * Duplicate - check for close
  1154.  */
  1155.  
  1156.     if ((iop->io_flag & IOTYPE) == IODUP)
  1157.     {
  1158.     if ((cp[1]) || (!isdigit (*cp) && (*cp != CHAR_CLOSE_FD)))
  1159.     {
  1160.         ShellErrorMessage ("illegal >& argument (%s)", cp);
  1161.         return TRUE;
  1162.     }
  1163.  
  1164.     if (*cp == CHAR_CLOSE_FD)
  1165.     {
  1166.         iop->io_flag &= ~IOTYPE;
  1167.         iop->io_flag |= IOCLOSE;
  1168.     }
  1169.     }
  1170.  
  1171. /*
  1172.  * When writing to /dev/???, we have to cheat because MSDOS appears to
  1173.  * have a problem with /dev/ files after find_first/find_next.
  1174.  */
  1175.  
  1176. #if (OS_TYPE != OS_UNIX)
  1177.     if (((iop->io_flag & IOTYPE) == IOWRITE) &&
  1178.     (strnicmp (cp, DeviceNameHeader, LEN_DEVICE_NAME_HEADER) == 0))
  1179.     {
  1180.     iop->io_flag &= ~IOTYPE;
  1181.     iop->io_flag |= IOCAT;
  1182.     }
  1183. #endif
  1184.  
  1185. /* Open the file in the appropriate mode */
  1186.  
  1187.     switch (iop->io_flag & IOTYPE)
  1188.     {
  1189.     case IOREAD:                /* <            */
  1190.         u = S_open (FALSE, cp, O_RDONLY);
  1191.         break;
  1192.  
  1193.     case IOHERE:                /* <<            */
  1194.         u = OpenHereFile (iop->io_name, C2bool (iop->io_flag & IOEVAL));
  1195.         cp = "here file";
  1196.         break;
  1197.  
  1198.     case IORDWR:                /* <>            */
  1199.         if (CheckForRestrictedShell (cp))
  1200.         return TRUE;
  1201.  
  1202.         u = S_open (FALSE, cp, O_RDWR);
  1203.         break;
  1204.  
  1205.     case IOCAT:                /* >>            */
  1206.         if (CheckForRestrictedShell (cp))
  1207.         return TRUE;
  1208.  
  1209.         if ((u = S_open (FALSE, cp, O_WRONLY | O_BINARY)) >= 0)
  1210.         {
  1211.         lseek (u, 0L, SEEK_END);
  1212.         break;
  1213.         }
  1214.  
  1215.     case IOWRITE:                /* >            */
  1216.         if (CheckForRestrictedShell (cp))
  1217.         return TRUE;
  1218.  
  1219.         if ((ShellGlobalFlags & FLAGS_NOCLOBER) &&
  1220.         (!(iop->io_flag & IOCLOBBER)) &&
  1221.         (S_access (CheckDOSFileName (cp), F_OK)))
  1222.         {
  1223.         u = -1;
  1224.         break;
  1225.         }
  1226.  
  1227.         u = S_open (FALSE, cp, O_CMASK);
  1228.         break;
  1229.  
  1230.     case IODUP:                /* >&            */
  1231.         if (CheckForRestrictedShell (cp))
  1232.         return TRUE;
  1233.  
  1234.         u = S_dup2 (*cp - '0', iop->io_unit);
  1235.         break;
  1236.  
  1237.     case IOCLOSE:                /* >-            */
  1238.         if ((iop->io_unit >= STDIN_FILENO) &&
  1239.         (iop->io_unit <= STDERR_FILENO))
  1240.         S_dup2 (-1, iop->io_unit);
  1241.  
  1242.         S_close (iop->io_unit, TRUE);
  1243.         return FALSE;
  1244.     }
  1245.  
  1246.     if (u < 0)
  1247.     {
  1248.     PrintWarningMessage (LIT_3Strings, cp, "cannot ", msg);
  1249.     return TRUE;
  1250.     }
  1251.  
  1252.     else if (u != iop->io_unit)
  1253.     {
  1254.     S_dup2 (u, iop->io_unit);
  1255.     S_close (u, TRUE);
  1256.     }
  1257.  
  1258.     return FALSE;
  1259. }
  1260.  
  1261. /*
  1262.  * -x flag - echo command to be executed
  1263.  */
  1264.  
  1265. static void F_LOCAL EchoCurrentCommand (char **wp)
  1266. {
  1267.     int        i;
  1268.  
  1269.     if ((CurrentFunction != (FunctionList *)NULL) && CurrentFunction->Traced)
  1270.     fprintf (stderr, "%s:", CurrentFunction->tree->str);
  1271.  
  1272.     feputs (GetVariableAsString (PS4, TRUE));
  1273.  
  1274.     for (i = 0; wp[i] != NOWORD; i++)
  1275.     {
  1276.     if (i)
  1277.         feputc (CHAR_SPACE);
  1278.  
  1279.     feputs (wp[i]);
  1280.     }
  1281.  
  1282.     feputc (CHAR_NEW_LINE);
  1283. }
  1284.  
  1285. /*
  1286.  * Set up the status on exit from a command
  1287.  */
  1288.  
  1289. static int F_LOCAL    SetCommandReturnStatus (int s)
  1290. {
  1291.     SetVariableFromNumeric (StatusVariable, (long) abs (ExitStatus = s));
  1292.     return s;
  1293. }
  1294.  
  1295. /*
  1296.  * Execute a command
  1297.  */
  1298.  
  1299. int ExecuteACommand (char **argv, int mode)
  1300. {
  1301.     int        RetVal;
  1302.  
  1303.     CheckProgramMode (*argv, &ExecProcessingMode);
  1304.     RetVal = EnvironExecute (argv, 0);
  1305.  
  1306.     if (ExecProcessingMode.Flags != EP_ENVIRON)
  1307.     RetVal = LocalExecve (argv, BuildCommandEnvironment (), mode);
  1308.  
  1309.     return RetVal;
  1310. }
  1311.  
  1312. /*
  1313.  * PATH-searching interface to execve.
  1314.  */
  1315.  
  1316. static int F_LOCAL LocalExecve (char **argv, char **envp, int ForkAction)
  1317. {
  1318.     int            res = -1;        /* Result        */
  1319.     char        *p_name;        /* Program name        */
  1320.     int            i;
  1321.  
  1322. /* Clear the DosExec Failname field */
  1323.  
  1324. #if (OS_TYPE == OS_OS2) 
  1325.     *FailName = 0;
  1326. #endif
  1327.  
  1328. #if (OS_TYPE != OS_DOS) 
  1329.     OS_DosExecPgmReturnCode = 0;
  1330. #endif
  1331.  
  1332. /* If the environment is null - It is too big - error */
  1333.  
  1334.     if (envp == NOWORDS)
  1335.     errno = E2BIG;
  1336.  
  1337.     else if ((p_name = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
  1338.     errno = ENOMEM;
  1339.  
  1340.     else
  1341.     {
  1342. /* Start off on the search path for the executable file */
  1343.  
  1344.     switch (i = FindLocationOfExecutable (p_name, argv[0]))
  1345.     {
  1346.         case EXTENSION_EXECUTABLE:
  1347.         res = ExecuteProgram (p_name, argv, envp, ForkAction);
  1348.         break;
  1349.  
  1350. /* Script file */
  1351.  
  1352.         case EXTENSION_BATCH:
  1353.         case EXTENSION_SHELL_SCRIPT:
  1354.         res = ExecuteScriptFile (p_name, argv, envp, ForkAction,
  1355.                      (bool)(i == EXTENSION_SHELL_SCRIPT));
  1356.         break;
  1357.     }
  1358.  
  1359.     if (res != -1)
  1360.     {
  1361.         TrackAllCommands (p_name, *argv);
  1362.         return res;
  1363.     }
  1364.     }
  1365.  
  1366. /* If no fork - exit */
  1367.  
  1368.     if (ForkAction & EXEC_WITHOUT_FORK)
  1369.     {
  1370.     PrintLoadError (*argv);
  1371.     FinalExitCleanUp (-1);
  1372.     }
  1373.  
  1374.     return -1;
  1375. }
  1376.  
  1377. /*
  1378.  * Exec or spawn the program ?
  1379.  */
  1380.  
  1381. static int F_LOCAL ExecuteProgram (char    *path,
  1382.                    char **parms,
  1383.                    char **envp,
  1384.                    int  ForkAction)
  1385. {
  1386.     int            res;
  1387. #ifdef OS_SWAPPING
  1388.     char        *ep;
  1389.     unsigned int    size = 0;
  1390.     int            serrno;
  1391.     unsigned int    c_cur = (unsigned int)(_psp - 1);
  1392.     struct MCB_list    *mp = (struct MCB_list *)((unsigned long)c_cur << 16L);
  1393. #endif
  1394.  
  1395. /* Check we have access to the file */
  1396.  
  1397.     if (!S_access (path, F_OK))
  1398.     return SetCommandReturnStatus (-1);
  1399.  
  1400. /* Process the command line.  If no swapping, we have executed the program */
  1401.  
  1402.     res = BuildCommandLine (path, parms, envp, ForkAction);
  1403.  
  1404. #if (OS_TYPE != OS_DOS) || (OS_SIZE == OS_32)
  1405.     SetWindowName ((char *)NULL);
  1406.     ClearExtendedLineFile ();
  1407.     return SetCommandReturnStatus (res);
  1408.  
  1409. #else
  1410.     if ((ExecProcessingMode.Flags & EP_NOSWAP) || (Swap_Mode == SWAP_OFF) ||
  1411.     res)
  1412.     {
  1413.     ClearExtendedLineFile ();
  1414.     return SetCommandReturnStatus (res);
  1415.     }
  1416.  
  1417. /* Find the length of the swap area */
  1418.  
  1419.     while ((mp = (struct MCB_list *)((unsigned long)c_cur << 16L))->MCB_type
  1420.         == MCB_CON)
  1421.     {
  1422.     if (c_cur >= 0x9ffe)
  1423.         break;
  1424.  
  1425.     if ((mp->MCB_pid != _psp) && (mp->MCB_pid != 0) &&
  1426.         (mp->MCB_type != MCB_END))
  1427.     {
  1428.         ClearExtendedLineFile ();
  1429.         PrintErrorMessage ("Fatal: Memory chain corrupt");
  1430.         return SetCommandReturnStatus (-1);
  1431.     }
  1432.  
  1433.     c_cur += (mp->MCB_len + 1);
  1434.     size += mp->MCB_len + 1;
  1435.     }
  1436.  
  1437. /*
  1438.  * Convert swap size from paragraphs to 16K blocks.
  1439.  */
  1440.  
  1441.     if (size == 0)
  1442.     size = mp->MCB_len + 1;
  1443.  
  1444.     SW_Blocks = (size / 0x0400) + 1;
  1445.     SW_SBlocks = ((size - etext + _psp - 1) / 0x0400) + 1;
  1446.  
  1447. /*
  1448.  * Minimum Environment space
  1449.  */
  1450.  
  1451.     SW_MinESpace = (unsigned int)GetVariableAsNumeric ("ENVIRONMENTSPACE");
  1452.  
  1453. /* OK Now we've set up the FCB's, command line and opened the swap file.
  1454.  * Get some sys info for the swapper and execute my little assembler
  1455.  * function to swap us out
  1456.  */
  1457.  
  1458. /* Ok - 3 methods of swapping.  Set up the program to execute */
  1459.  
  1460.     strcpy (path_line, path);
  1461.  
  1462. /* If expanded memory - try that */
  1463.  
  1464.     if ((Swap_Mode & SWAP_EXPAND) && Get_EMS_Driver ())
  1465.     {
  1466.     SW_Mode = 3;            /* Set Expanded memory swap    */
  1467.  
  1468.     if ((res = SwapToMemory (SWAP_EXPAND, envp)) != -2)
  1469.         return CheckForExecOnly (*parms, res,
  1470.                      (ForkAction & EXEC_WITHOUT_FORK));
  1471.     }
  1472.  
  1473.     if ((Swap_Mode & SWAP_EXTEND) && Get_XMS_Driver ())
  1474.     {
  1475.     SW_Mode = (SW_fp == -1) ? 2 : 4;/* Set Extended memory or XMS driver */
  1476.  
  1477.     if ((res = SwapToMemory (SWAP_EXTEND, envp)) != -2)
  1478.         return CheckForExecOnly (*parms, res,
  1479.                      (ForkAction & EXEC_WITHOUT_FORK));
  1480.  
  1481.     Swap_Mode &= ~SWAP_EXTEND;
  1482.     }
  1483.  
  1484. /* Try the disk if available */
  1485.  
  1486.     if (Swap_Mode & SWAP_DISK)
  1487.     {
  1488.     SW_Pwrite = 0;
  1489.  
  1490.     if (Swap_File == (char *)NULL)
  1491.         SW_fp = S_open (FALSE, (ep = GenerateTemporaryFileName ()),
  1492.                 O_SMASK);
  1493.  
  1494.     else
  1495.     {
  1496.         SW_fp = S_open (FALSE, Swap_File, O_SaMASK);
  1497.         SW_Pwrite = 1;
  1498.     }
  1499.  
  1500.     if (SW_fp < 0)
  1501.         return SwapToDiskError (ENOSPC, NoSwapFiles);
  1502.  
  1503. /* Save the swap file name ? */
  1504.  
  1505.     if ((Swap_File == (char *)NULL) &&
  1506.         ((Swap_File = StringSave (ep)) == null))
  1507.         Swap_File = (char *)NULL;
  1508.  
  1509.     SW_Mode = 1;            /* Set Disk file swap        */
  1510.  
  1511. /* Seek to correct location */
  1512.  
  1513.     if (SW_Pwrite)
  1514.     {
  1515.         long    loc = (long)(etext - _psp + 1) * 16L;
  1516.  
  1517.         if (lseek (SW_fp, loc, SEEK_SET) != loc)
  1518.         return SwapToDiskError (ENOSPC, NoSwapFiles);
  1519.     }
  1520.  
  1521. /* Execute the program */
  1522.  
  1523.     res = SpawnProcess (envp);
  1524.  
  1525. /* Close the extended command line file */
  1526.  
  1527.     ClearExtendedLineFile ();
  1528.  
  1529. /* Check for out of swap space */
  1530.  
  1531.     if (res == -2)
  1532.     {
  1533.         SwapToDiskError (errno, "Swap file write failed");
  1534.         return CheckForExecOnly (*parms, res,
  1535.                      (ForkAction & EXEC_WITHOUT_FORK));
  1536.     }
  1537.  
  1538. /* Close the swap file */
  1539.  
  1540.     serrno = errno;
  1541.     S_close (SW_fp, TRUE);
  1542.     errno = serrno;
  1543.  
  1544. /* Return the result */
  1545.  
  1546.     return CheckForExecOnly (*parms, SetCommandReturnStatus (res),
  1547.                  (ForkAction & EXEC_WITHOUT_FORK));
  1548.     }
  1549.  
  1550. /* No swapping available - give up */
  1551.  
  1552.     ClearExtendedLineFile ();
  1553.     PrintErrorMessage ("All Swapping methods failed");
  1554.     Swap_Mode = SWAP_OFF;
  1555.     errno = ENOSPC;
  1556.     return SetCommandReturnStatus (-1);
  1557. #endif
  1558. }
  1559.  
  1560. /*
  1561.  * OS2 and WinNT and DOS 32 do not require swapping
  1562.  *
  1563.  * Check for Exec only.  We check on exec and do a spawn and then exit
  1564.  * to solve some memory allocation problems and because I can't be bothered
  1565.  * with complexities of doing an exec in sh0.asm.
  1566.  */
  1567.  
  1568. #ifdef OS_SWAPPING
  1569. static int F_LOCAL CheckForExecOnly (char *pgm, int res, int ExecMode)
  1570. {
  1571.     if (ExecMode)
  1572.     {
  1573.     if (res >= 0)
  1574.         FinalExitCleanUp (0);
  1575.  
  1576.     PrintLoadError (pgm);
  1577.     FinalExitCleanUp (-1);
  1578.     }
  1579.  
  1580. /* Convert swap error to general error */
  1581.  
  1582.     else if (res == -2)
  1583.     res = -1;
  1584.     
  1585.     return res;
  1586. }
  1587.  
  1588. /*
  1589.  * Get the XMS Driver information
  1590.  */
  1591.  
  1592. static bool F_LOCAL Get_XMS_Driver (void)
  1593. {
  1594.     union REGS        or;
  1595.     struct SREGS    sr;
  1596.     unsigned int    SW_EMsize;    /* Number of extend memory blks    */
  1597.  
  1598. /* Get max Extended memory pages, and convert to 16K blocks.  If Extended
  1599.  * memory swapping disabled, set to zero
  1600.  */
  1601.  
  1602.     SW_fp = -1;                /* Set EMS/XMS handler not    */
  1603.                     /* defined            */
  1604.  
  1605. /* Is a XMS memory driver installed */
  1606.  
  1607.     or.x.REG_AX = 0x4300;
  1608.     SystemInterrupt (0x2f, &or, &or);
  1609.  
  1610.     if (or.h.al != 0x80)
  1611.     {
  1612.     or.x.REG_AX = 0x8800;
  1613.     SystemInterrupt (0x15, &or, &or);
  1614.     SW_EMsize = or.x.REG_AX / 16;
  1615.  
  1616.     if ((SW_EMsize <= SW_Blocks) ||
  1617.          (((long)(SW_EMstart - 0x100000L) +
  1618.           ((long)(SW_Blocks - SW_EMsize) * 16L * 1024L)) < 0L))
  1619.         return XMS_error (MS_Space, 0);
  1620.  
  1621.     else
  1622.         return TRUE;
  1623.     }
  1624.  
  1625. /* Get the driver interface */
  1626.  
  1627.     or.x.REG_AX = 0x4310;
  1628.     SystemExtendedInterrupt (0x2f, &or, &or, &sr);
  1629.     SW_XMS_Driver = (unsigned long)((unsigned long)(sr.es) << 16L |
  1630.                     (unsigned long)(or.x.REG_BX));
  1631.  
  1632. /* Support for version 3 of XMS driver */
  1633.  
  1634.     if ((SW_XMS_Gversion () & 0xff00) < 0x0200)
  1635.     return !FL_TEST (FLAG_WARNING)
  1636.         ? XMS_error ("Warning: %s Version < 2", 0) : FALSE;
  1637.  
  1638.     else if (SW_XMS_Available () < (SW_Blocks * 16))
  1639.     return !FL_TEST (FLAG_WARNING) ? XMS_error (MS_Space, 0) : FALSE;
  1640.  
  1641.     else if ((SW_fp = SW_XMS_Allocate (SW_Blocks * 16)) == -1)
  1642.     return XMS_error (MS_emsg, errno);
  1643.  
  1644.     return TRUE;
  1645. }
  1646.  
  1647. /* Get the EMS Driver information */
  1648.  
  1649. static bool F_LOCAL Get_EMS_Driver (void)
  1650. {
  1651.     union REGS        or;
  1652.     struct SREGS    sr;
  1653.     char        *sp;
  1654.  
  1655. /* Set EMS/XMS handler not defined */
  1656.  
  1657.     SW_fp = -1;
  1658.  
  1659.     or.x.REG_AX = 0x3567;
  1660.     DosExtendedInterrupt (&or, &or, &sr);
  1661.  
  1662.     sp = (char *)((unsigned long)(sr.es) << 16L | 10L);
  1663.  
  1664. /* If not there - disable */
  1665.  
  1666.     if (memcmp ("EMMXXXX0", sp, 8) != 0)
  1667.     return !FL_TEST (FLAG_WARNING)
  1668.             ? EMS_error ("Warning: %s not available", 0) : FALSE;
  1669.  
  1670.     or.h.ah = 0x40;            /* Check status            */
  1671.     SystemInterrupt (0x67, &or, &or);
  1672.  
  1673.     if (or.h.ah != 0)
  1674.     return EMS_error (MS_emsg, or.h.ah);
  1675.  
  1676. /* Check version greater than 3.2 */
  1677.  
  1678.     or.h.ah = 0x46;
  1679.     SystemInterrupt (0x67, &or, &or);
  1680.  
  1681.     if ((or.h.ah != 0) || (or.h.al < 0x32))
  1682.     return !FL_TEST (FLAG_WARNING)
  1683.             ? EMS_error ("Warning: %s Version < 3.2", 0) : FALSE;
  1684.  
  1685. /*  get page frame address */
  1686.  
  1687.     or.h.ah = 0x41;
  1688.     SystemInterrupt (0x67, &or, &or);
  1689.  
  1690.     if (or.h.ah != 0)
  1691.     return EMS_error (MS_emsg, or.h.ah);
  1692.  
  1693.     SW_EMSFrame = or.x.REG_BX;        /* Save the page frame        */
  1694.  
  1695. /* Get the number of pages required */
  1696.  
  1697.     or.h.ah = 0x43;
  1698.     or.x.REG_BX = SW_Blocks;
  1699.     SystemInterrupt (0x67, &or, &or);
  1700.  
  1701.     if (or.h.ah == 0x088)
  1702.     return EMS_error (MS_Space, 0);
  1703.  
  1704.     if (or.h.ah != 0)
  1705.     return EMS_error (MS_emsg, or.h.ah);
  1706.  
  1707. /* Save the EMS Handler */
  1708.  
  1709.     SW_fp = or.x.REG_DX;
  1710.  
  1711. /* save EMS page map */
  1712.  
  1713.     or.h.ah = 0x47;
  1714.     or.x.REG_DX = SW_fp;
  1715.     SystemInterrupt (0x67, &or, &or);
  1716.  
  1717.     return (or.h.ah != 0) ? EMS_error (MS_emsg, or.h.ah) : TRUE;
  1718. }
  1719.  
  1720. /* Print EMS error message */
  1721.  
  1722. static bool F_LOCAL EMS_error (char *s, int v)
  1723. {
  1724.     PrintWarningMessage (s, "EMS", v);
  1725.     Swap_Mode &= ~(SWAP_EXPAND);
  1726.     EMS_Close ();
  1727.     return FALSE;
  1728. }
  1729.  
  1730. /* Print XMS error message */
  1731.  
  1732. static bool F_LOCAL XMS_error (char *s, int v)
  1733. {
  1734.     PrintWarningMessage (s, "XMS", v);
  1735.     Swap_Mode &= ~(SWAP_EXTEND);
  1736.     XMS_Close ();
  1737.     return FALSE;
  1738. }
  1739.  
  1740. /* If the XMS handler is defined - close it */
  1741.  
  1742. static int F_LOCAL XMS_Close (void)
  1743. {
  1744.     int        res = 0;
  1745.  
  1746. /* Release XMS page */
  1747.  
  1748.     if (SW_fp != -1)
  1749.     res = SW_XMS_Free (SW_fp);
  1750.  
  1751.     SW_fp = -1;
  1752.     return res;
  1753. }
  1754.  
  1755. /* If the EMS handler is defined - close it */
  1756.  
  1757. static int F_LOCAL EMS_Close (void)
  1758. {
  1759.     union REGS        or;
  1760.     int            res = 0;
  1761.  
  1762.     if (SW_fp == -1)
  1763.     return 0;
  1764.  
  1765. /* Restore EMS page */
  1766.  
  1767.     or.h.ah = 0x48;
  1768.     or.x.REG_DX = SW_fp;
  1769.     SystemInterrupt (0x67, &or, &or);
  1770.  
  1771.     if (or.h.ah != 0)
  1772.     res = or.h.al;
  1773.  
  1774.     or.h.ah = 0x45;
  1775.     or.x.REG_DX = SW_fp;
  1776.     SystemInterrupt (0x67, &or, &or);
  1777.  
  1778.     SW_fp = -1;
  1779.     return (res) ? res : or.h.ah;
  1780. }
  1781. #endif
  1782.  
  1783. /* Set up command line.  If the EXTENDED_LINE variable is set, we create
  1784.  * a temporary file, write the argument list (one entry per line) to the
  1785.  * this file and set the command line to @<filename>.  If NOSWAPPING, we
  1786.  * execute the program because I have to modify the argument line
  1787.  */
  1788.  
  1789. static int F_LOCAL BuildCommandLine (char *path,
  1790.                      char **argv,
  1791.                      char **envp,
  1792.                      int  ForkAction)
  1793. {
  1794.     char        **pl = argv;
  1795.     FILE        *fd;
  1796.     bool        found;
  1797.     char        *new_args[3];
  1798. #ifndef OS_SWAPPING
  1799.     char        cmd_line[FFNAME_MAX];
  1800. #endif
  1801.  
  1802. /* Translate process name to MSDOS format */
  1803.  
  1804.     if ((GenerateFullExecutablePath (path) == (char *)NULL) ||
  1805.     (!GetApplicationType (path)))
  1806.     return -1;
  1807.  
  1808. /* If this is a windows program - special */
  1809.  
  1810. #if (OS_TYPE != OS_UNIX)
  1811.     if ((ApplicationType == EXETYPE_DOS_GUI) && (BaseOS != BASE_OS_NT))
  1812.         return ExecuteWindows (path, argv, envp, ForkAction);
  1813. #endif
  1814.  
  1815. /* Extended command line processing */
  1816.  
  1817.     Extend_file = (char *)NULL;        /* Set no file        */
  1818.     found = C2bool ((ExecProcessingMode.Flags & EP_UNIXMODE) ||
  1819.             (ExecProcessingMode.Flags & EP_DOSMODE));
  1820.  
  1821. /* Set up a blank command line */
  1822.  
  1823.     cmd_line[0] = 0;
  1824.     cmd_line[1] = CHAR_RETURN;
  1825.  
  1826. /* If there are no parameters, or they fit in the DOS command line
  1827.  * - start the process */
  1828.  
  1829.     if ((*(++pl) == (char *)NULL) || CheckParameterLength (pl))
  1830.     return StartTheProcess (path, argv, envp, ForkAction);
  1831.  
  1832. /* If we can use an alternative approach - indirect files, use it */
  1833.  
  1834.     else if (found)
  1835.     {
  1836.     char    **pl1 = pl;
  1837.  
  1838. /* Check parameters don't contain a re-direction parameter */
  1839.  
  1840.     while (*pl1 != NOWORD)
  1841.     {
  1842.         if (**(pl1++) == CHAR_INDIRECT)
  1843.         {
  1844.         found = FALSE;
  1845.         break;
  1846.         }
  1847.     }
  1848.  
  1849. /* If we find it - create a temporary file and write the stuff */
  1850.  
  1851.     if ((found) &&
  1852.         ((fd = FOpenFile (Extend_file = GenerateTemporaryFileName (),
  1853.                   sOpenWriteMode)) != (FILE *)NULL))
  1854.     {
  1855.         if ((Extend_file = StringSave (Extend_file)) == null)
  1856.         Extend_file = (char *)NULL;
  1857.  
  1858. /* Copy to end of list */
  1859.  
  1860.         do
  1861.         {
  1862.         if (!WriteToExtendedFile (fd, *pl))
  1863.             return -1;
  1864.         } while (*(pl++) != NOWORD);
  1865.  
  1866. /* Set up cmd_line[1] to contain the filename */
  1867.  
  1868. #ifdef OS_SWAPPING
  1869.         memset (cmd_line, 0, CMD_LINE_MAX);
  1870. #else
  1871.         memset (cmd_line, 0, FFNAME_MAX);
  1872. #endif
  1873.         cmd_line[1] = CHAR_SPACE;
  1874.         cmd_line[2] = CHAR_INDIRECT;
  1875.         strcpy (&cmd_line[3], Extend_file);
  1876.         cmd_line[0] = (char)(strlen (Extend_file) + 2);
  1877.  
  1878. /* Correctly terminate cmd_line in no swap mode */
  1879.  
  1880. #ifdef OS_SWAPPING
  1881.         if (!(ExecProcessingMode.Flags & EP_NOSWAP) &&
  1882.         (Swap_Mode != SWAP_OFF))
  1883.         cmd_line[cmd_line[0] + 2] = CHAR_RETURN;
  1884. #endif
  1885.  
  1886. /* If the name in the file is in upper case - use \ for separators */
  1887.  
  1888.         if (ExecProcessingMode.Flags & EP_DOSMODE)
  1889.         PATH_TO_DOS (&cmd_line[2]);
  1890.  
  1891. /* OK we are ready to execute */
  1892.  
  1893. #ifdef OS_SWAPPING
  1894.         if ((ExecProcessingMode.Flags & EP_NOSWAP) ||
  1895.         (Swap_Mode == SWAP_OFF) || (ForkAction & EXEC_WITHOUT_FORK))
  1896.         {
  1897. #endif
  1898.         new_args[0] = *argv;
  1899.         new_args[1] = &cmd_line[2];
  1900.         new_args[2] = (char *)NULL;
  1901.  
  1902.         return StartTheProcess (path, new_args, envp, ForkAction);
  1903. #ifdef OS_SWAPPING
  1904.         }
  1905.  
  1906.         else
  1907.         return 0;
  1908. #endif
  1909.     }
  1910.     }
  1911.  
  1912.     return -1;
  1913. }
  1914.  
  1915. /*
  1916.  * Clear Extended command line file
  1917.  */
  1918.  
  1919. void ClearExtendedLineFile (void)
  1920. {
  1921.     if (Extend_file != (char *)NULL)
  1922.     {
  1923.     unlink (Extend_file);
  1924.     ReleaseMemoryCell ((void *)Extend_file);
  1925.     }
  1926.  
  1927.     Extend_file = (char *)NULL;
  1928. }
  1929.  
  1930. /*
  1931.  * Clear Disk swap file file
  1932.  */
  1933.  
  1934. #ifdef OS_SWAPPING
  1935. void ClearSwapFile (void)
  1936. {
  1937.     if (SW_fp >= 0)
  1938.     S_close (SW_fp, TRUE);
  1939.  
  1940.     if (Swap_File != (char *)NULL)
  1941.     {
  1942.     unlink (Swap_File);
  1943.     ReleaseMemoryCell ((void *)Swap_File);
  1944.     }
  1945.  
  1946.     SW_fp = -1;
  1947.     Swap_File = (char *)NULL;
  1948. }
  1949. #endif
  1950.  
  1951. /*
  1952.  * Convert the executable path to the full path name
  1953.  */
  1954.  
  1955. char    *GenerateFullExecutablePath (char *path)
  1956. {
  1957.     char        cpath[PATH_MAX + 6];
  1958.     char        npath[FFNAME_MAX + 2];
  1959.     char        n1path[PATH_MAX + 6];
  1960.     char        *p;
  1961.     int            drive;
  1962.  
  1963.     PATH_TO_UPPER_CASE (path);
  1964.  
  1965. /* Get the current path */
  1966.  
  1967.     S_getcwd (cpath, 0);
  1968.     strcpy (npath, cpath);
  1969.  
  1970. /* In current directory ? */
  1971.  
  1972.     if ((p = FindLastPathCharacter (path)) == (char *)NULL)
  1973.     {
  1974.      p = path;
  1975.  
  1976. /* Check for a:program case */
  1977.  
  1978.      if (IsDriveCharacter (*(p + 1)))
  1979.      {
  1980.         p += 2;
  1981.  
  1982. /* Get the path of the other drive */
  1983.  
  1984.         S_getcwd (npath, GetDriveNumber (*path));
  1985.      }
  1986.     }
  1987.  
  1988. /* In root directory */
  1989.  
  1990.     else if ((p - path) == 0)
  1991.     {
  1992.     ++p;
  1993.     strcpy (npath, RootDirectory);
  1994.     *npath = *path;
  1995.     *npath = *cpath;
  1996.     }
  1997.  
  1998.     else if (((p - path) == 2) && IsDriveCharacter (*(path + 1)))
  1999.     {
  2000.     ++p;
  2001.     strcpy (npath, RootDirectory);
  2002.     *npath = *path;
  2003.     }
  2004.  
  2005. /* Find the directory */
  2006.  
  2007.     else
  2008.     {
  2009.     *(p++) = 0;
  2010.  
  2011. /* Change to the directory containing the executable */
  2012.  
  2013.     drive = IsDriveCharacter (*(path + 1))
  2014.             ? GetDriveNumber (*path)
  2015. #if (OS_TYPE == OS_OS2) && !defined (__WATCOMC__)
  2016.             : _getdrive ();
  2017. #else
  2018.             : 0;
  2019. #endif
  2020.  
  2021. /* Save the current directory on this drive */
  2022.  
  2023.     S_getcwd (n1path, drive);
  2024.  
  2025. /* Find the directory we want */
  2026.  
  2027.     if (!S_chdir (path))
  2028.         return (char *)NULL;
  2029.  
  2030.     S_getcwd (npath, drive);        /* Save its full name */
  2031.     S_chdir (n1path);            /* Restore the original */
  2032.  
  2033. /* Restore our original directory */
  2034.  
  2035.     if (!S_chdir (cpath))
  2036.         return (char *)NULL;
  2037.     }
  2038.  
  2039.     if (!IsPathCharacter (npath[strlen (npath) - 1]))
  2040.     strcat (npath, DirectorySeparator);
  2041.  
  2042.     strcat (npath, p);
  2043.     return PATH_TO_DOS (strcpy (path, npath));
  2044. }
  2045.  
  2046. /*
  2047.  * Find the number of values to use for a for or select statement
  2048.  */
  2049.  
  2050. static char ** F_LOCAL FindNumberOfValues (char **wp, int *Count)
  2051. {
  2052.  
  2053. /* select/for x do...done - use the parameter values.  Need to know how many as
  2054.  * it is not a NULL terminated array
  2055.  */
  2056.  
  2057.     if (wp == NOWORDS)
  2058.     {
  2059.     if ((*Count = ParameterCount) < 0)
  2060.         *Count = 0;
  2061.  
  2062.     return ParameterArray + 1;
  2063.     }
  2064.  
  2065. /* select/for x in y do...done - find the start of the variables and
  2066.  * use them all
  2067.  */
  2068.  
  2069.     *Count = CountNumberArguments (wp);
  2070.  
  2071.     return wp;
  2072. }
  2073.  
  2074. /*
  2075.  * Count the number of entries in an array
  2076.  */
  2077.  
  2078. int    CountNumberArguments (char **wp)
  2079. {
  2080.     int        Count = 0;
  2081.  
  2082.     while (*(wp++) != NOWORD)
  2083.     Count++;
  2084.  
  2085.     return Count;
  2086. }
  2087.  
  2088. /*
  2089.  * Write a command string to the extended file
  2090.  */
  2091.  
  2092. static bool F_LOCAL WriteToExtendedFile (FILE *fd, char *string)
  2093. {
  2094.     char    *sp = string;
  2095.     char    *cp = string;
  2096.     bool    WriteOk = TRUE;
  2097.  
  2098.     if (string == (char *)NULL)
  2099.     {
  2100.     if (CloseFile (fd) != EOF)
  2101.         return TRUE;
  2102.  
  2103.     WriteOk = FALSE;
  2104.     }
  2105.  
  2106.     else if (strlen (string))
  2107.     {
  2108.  
  2109. /* Write the string, converting newlines to backslash newline */
  2110.  
  2111.     while (WriteOk && (cp != (char *)NULL))
  2112.     {
  2113.         if ((cp = strchr (sp, CHAR_NEW_LINE)) != (char *)NULL)
  2114.         *cp = 0;
  2115.  
  2116.         if (fputs (sp, fd) == EOF)
  2117.         WriteOk = FALSE;
  2118.  
  2119.         else if (cp != (char *)NULL)
  2120.         WriteOk = C2bool (fputs ("\\\n", fd) != EOF);
  2121.  
  2122.         sp = cp + 1;
  2123.     }
  2124.     }
  2125.  
  2126.     if (WriteOk && (fputc (CHAR_NEW_LINE, fd) != EOF))
  2127.     return TRUE;
  2128.  
  2129.     CloseFile (fd);
  2130.     ClearExtendedLineFile ();
  2131.     errno = ENOSPC;
  2132.     return FALSE;
  2133. }
  2134.  
  2135. /*
  2136.  * Execute or spawn the process
  2137.  */
  2138.  
  2139. #ifdef OS_SWAPPING
  2140. static int F_LOCAL StartTheProcess (char *path, char **argv, char **envp,
  2141.                     int ForkAction)
  2142. {
  2143.     if (ForkAction & EXEC_WITHOUT_FORK)
  2144.     {
  2145.     DumpHistory ();                /* Exec - save history */
  2146.     ProcessSpaceInParameters (argv);
  2147.     return 0;
  2148.     }
  2149.  
  2150.     return ((ExecProcessingMode.Flags & EP_NOSWAP) || (Swap_Mode == SWAP_OFF))
  2151.         ? spawnve (P_WAIT, path, ProcessSpaceInParameters (argv), envp)
  2152.         : 0;
  2153. }
  2154. #endif
  2155.  
  2156. /* DOS, 32 bit version */
  2157.  
  2158. #if (OS_TYPE == OS_DOS) && (OS_SIZE == OS_32)
  2159. static int F_LOCAL StartTheProcess (char *path, char **argv, char **envp,
  2160.                     int ForkAction)
  2161. {
  2162.     int        retval;
  2163.  
  2164.     if (ForkAction & EXEC_WITHOUT_FORK)
  2165.     {
  2166.     DumpHistory ();                /* Exec - save history */
  2167.  
  2168.     if ((retval = spawnve (P_WAIT, path, ProcessSpaceInParameters (argv),
  2169.                        envp)) != -1)
  2170.         exit (retval);
  2171.  
  2172.     PrintErrorMessage ("unexpected return from exec (%d)", errno);
  2173.     return retval;
  2174.     }
  2175.  
  2176.     return spawnve (P_WAIT, path, ProcessSpaceInParameters (argv), envp);
  2177. }
  2178. #endif
  2179.  
  2180. /* OS/2 Version */
  2181.  
  2182. #if (OS_TYPE == OS_OS2)
  2183. static int F_LOCAL StartTheProcess (char *path, char **argv, char **envp,
  2184.                     int ForkAction)
  2185. {
  2186.     int            RetVal;
  2187.     STARTDATA        stdata;
  2188.  
  2189. /* Is this a start session option */
  2190.  
  2191.     if (SessionControlBlock != (STARTDATA *)NULL)
  2192.     return StartTheSession (SessionControlBlock, path, argv, envp,
  2193.                 ForkAction);
  2194.  
  2195. /* Exec ? */
  2196.  
  2197.     if (ForkAction & EXEC_WITHOUT_FORK)
  2198.     return OS_DosExecProgram (OLD_P_OVERLAY, path, argv, envp);
  2199.  
  2200.     if (ForkAction & (EXEC_SPAWN_DETACH | EXEC_SPAWN_NOWAIT |
  2201.               EXEC_SPAWN_IGNOREWAIT))
  2202.     {
  2203.     int    Mode = (ForkAction & EXEC_SPAWN_DETACH)
  2204.             ? P_DETACH
  2205.             : ((ForkAction & EXEC_SPAWN_IGNOREWAIT)
  2206.                ? P_NOWAITO
  2207.                : (FL_TEST (FLAG_SEPARATE_GROUP)
  2208.                  ? P_DETACH
  2209.                   : P_NOWAIT));
  2210.  
  2211.     RetVal = OS_DosExecProgram (Mode, path, argv, envp);
  2212.  
  2213. /* Remove the reference to the temporary file for background tasks */
  2214.  
  2215.     ReleaseMemoryCell ((void *)Extend_file);
  2216.     Extend_file = (char *)NULL;
  2217.  
  2218.     if ((RetVal != -1) && (Mode != P_NOWAITO))
  2219.     {
  2220.         if (InteractiveFlag && (source->type == STTY))
  2221.         {
  2222.             if (ForkAction & EXEC_SPAWN_DETACH)
  2223.             fprintf (stderr, "[0] Process %d detached\n", RetVal);
  2224.  
  2225.         else
  2226.         {
  2227.             PidInfo.Valid = TRUE;
  2228.             PidInfo.JobNo = AddNewJob (RetVal, 0, path);
  2229.             PidInfo.PidNo = RetVal;
  2230.         }
  2231.         }
  2232.  
  2233.         SetVariableFromNumeric ("!", RetVal);
  2234.         return 0;
  2235.     }
  2236.  
  2237.     return RetVal;
  2238.     }
  2239.  
  2240. /* In OS/2, we need the type of the program because PM programs have to be
  2241.  * started in a session (or at least that was the only way I could get them
  2242.  * to work).
  2243.  */
  2244.  
  2245. /* Do we need to start a new session for this program ? */
  2246.  
  2247.     if (((ApplicationType & EXETYPE_OS2_TYPE) == EXETYPE_OS2_GUI) ||
  2248.     (ApplicationType & EXETYPE_DOS))
  2249.     {
  2250.     if ((OS_VERS_N > 1) && (ApplicationType & EXETYPE_DOS))
  2251.         memcpy (&stdata, &DOS_SessionControlBlock, sizeof (STARTDATA));
  2252.  
  2253.     else
  2254.         memcpy (&stdata, &PM_SessionControlBlock, sizeof (STARTDATA));
  2255.  
  2256.     if (ForkAction & EXEC_WINDOWS)
  2257.         stdata.PgmControl = 0;
  2258.  
  2259.     return StartTheSession (&stdata, path, argv, envp, ForkAction);
  2260.     }
  2261.  
  2262. /* Just DosExecPgm it */
  2263.  
  2264.     return OS_DosExecProgram (P_WAIT, path, argv, envp);
  2265. }
  2266. #endif
  2267.  
  2268. /* NT Version */
  2269.  
  2270. #if (OS_TYPE == OS_NT)
  2271. static int F_LOCAL StartTheProcess (char *path, char **argv, char **envp,
  2272.                     int ForkAction)
  2273. {
  2274.     int            RetVal;
  2275.  
  2276. /* Exec ? */
  2277.  
  2278.     if (ForkAction & EXEC_WITHOUT_FORK)
  2279.     return OS_DosExecProgram (OLD_P_OVERLAY, path, argv, envp);
  2280.  
  2281.     if (ForkAction & (EXEC_SPAWN_DETACH | EXEC_SPAWN_NOWAIT |
  2282.               EXEC_SPAWN_IGNOREWAIT))
  2283.     {
  2284.     int    Mode = (ForkAction & EXEC_SPAWN_DETACH)
  2285.             ? P_DETACH
  2286.             : ((ForkAction & EXEC_SPAWN_IGNOREWAIT)
  2287.                ? P_NOWAITO
  2288.                : (FL_TEST (FLAG_SEPARATE_GROUP)
  2289.                  ? P_DETACH
  2290.                   : P_NOWAIT));
  2291.  
  2292.     RetVal = OS_DosExecProgram (Mode, path, argv, envp);
  2293.  
  2294. /* Remove the reference to the temporary file for background tasks */
  2295.  
  2296.     ReleaseMemoryCell ((void *)Extend_file);
  2297.     Extend_file = (char *)NULL;
  2298.  
  2299.     if ((RetVal != -1) && (Mode != P_NOWAITO))
  2300.     {
  2301.         if (InteractiveFlag && (source->type == STTY))
  2302.         {
  2303.             if (ForkAction & EXEC_SPAWN_DETACH)
  2304.             fprintf (stderr, "[0] Process %d detached\n", RetVal);
  2305.  
  2306.         else
  2307.         {
  2308.             PidInfo.Valid = TRUE;
  2309.             PidInfo.JobNo = AddNewJob (RetVal, 0, path);
  2310.             PidInfo.PidNo = RetVal;
  2311.         }
  2312.         }
  2313.  
  2314.         SetVariableFromNumeric ("!", RetVal);
  2315.         return 0;
  2316.     }
  2317.  
  2318.     else
  2319.         return RetVal;
  2320.     }
  2321.  
  2322. /* Just DosExecPgm it */
  2323.  
  2324.     return OS_DosExecProgram (P_WAIT, path, argv, envp);
  2325. }
  2326. #endif
  2327.  
  2328. /*
  2329.  * Start a session
  2330.  */
  2331.  
  2332. #if (OS_TYPE == OS_OS2)
  2333. static int F_LOCAL StartTheSession (STARTDATA    *SessionData,
  2334.                     char    *path,
  2335.                     char    **argv,
  2336.                     char    **envp,
  2337.                     int        ForkAction)
  2338. {
  2339.     OSCALL_PARAM    usType;
  2340.     OSCALL_PARAM    idSession;
  2341. #  if (OS_SIZE == OS_32)
  2342.     PID            pid;
  2343. #  else
  2344.     USHORT        pid;
  2345. #  endif
  2346.  
  2347. /*
  2348.  * For OS/2 2.x, we can start DOS sessions!!
  2349.  */
  2350.  
  2351.     if ((OS_VERS_N > 1) && (ApplicationType & EXETYPE_DOS))
  2352.     {
  2353.     if ((ForkAction & EXEC_WINDOWS) ||
  2354.         (SessionData->SessionType == SSF_TYPE_FULLSCREEN))
  2355.         SessionData->SessionType = SSF_TYPE_VDM;
  2356.  
  2357.     else
  2358.         SessionData->SessionType = SSF_TYPE_WINDOWEDVDM;
  2359.  
  2360.     if (SessionData->Environment == (PBYTE)1)
  2361.         SessionData->Environment = (PBYTE)NULL;
  2362.     }
  2363.  
  2364.     else if ((ApplicationType & EXETYPE_OS2_TYPE) == EXETYPE_OS2_GUI)
  2365.     SessionData->SessionType = SSF_TYPE_PM;
  2366.  
  2367.     SessionData->PgmName = path;
  2368.     SessionData->TermQ = SessionEndQName;    /* Queue name        */
  2369.  
  2370.     /*
  2371.      * Build the environment if the current value is 1
  2372.      */
  2373.  
  2374.     if ((SessionData->Environment == (PBYTE)1) &&
  2375.     ((SessionData->Environment = (PBYTE)BuildOS2String (envp, 0))
  2376.                    == (PBYTE)NULL))
  2377.     return -1;
  2378.  
  2379.     ProcessSpaceInParameters (argv);
  2380.  
  2381.     if ((SessionData->PgmInputs = (PBYTE)BuildOS2String (&argv[1], CHAR_SPACE))
  2382.                     == (PBYTE)NULL)
  2383.     return -1;
  2384.  
  2385. /*
  2386.  * Start the session.  Do not record if it is independent
  2387.  */
  2388.  
  2389.     DISABLE_HARD_ERRORS;
  2390. #  if (OS_SIZE == OS_16) && !defined (OS2_16_BUG)
  2391.     SessionData->Related = SSF_RELATED_INDEPENDENT;
  2392. #  endif
  2393.     usType = DosStartSession (SessionData, &idSession, &pid);
  2394.     ENABLE_HARD_ERRORS;
  2395.  
  2396.     if ((!usType) || (usType == ERROR_SMG_START_IN_BACKGROUND))
  2397.     {
  2398.     if (InteractiveFlag && (source->type == STTY))
  2399.     {
  2400.         if (SessionData->Related == SSF_RELATED_INDEPENDENT)
  2401.         fprintf (stderr, "[0] Independent Session %d started\n",
  2402.              idSession);
  2403.  
  2404.         else
  2405.         fprintf (stderr, "[%d] Session %d (PID %d) started\n",
  2406.              AddNewJob (pid, idSession, path), idSession, pid);
  2407.  
  2408.         if (usType)
  2409.         fprintf (stderr, "%s\n", GetOSSystemErrorMessage (usType));
  2410.     }
  2411.  
  2412.         SetVariableFromNumeric ("!", pid);
  2413.     return 0;
  2414.     }
  2415.  
  2416.     else
  2417.     {
  2418.     strcpy (FailName, GetOSSystemErrorMessage (usType));
  2419.  
  2420.     errno = ENOENT;
  2421.     return -1;
  2422.     }
  2423. }
  2424. #endif
  2425.  
  2426. /*
  2427.  * Build the OS2 format <value>\0<value>\0 etc \0
  2428.  */
  2429.  
  2430. char    *BuildOS2String (char **Array, char sep)
  2431. {
  2432.     int        i = 0;
  2433.     int        Length = 0;
  2434.     char    *Output;
  2435.     char    *sp, *cp;
  2436.  
  2437. /* Find the total data length */
  2438.  
  2439.     while ((sp = Array[i++]) != NOWORD)
  2440.     Length += strlen (sp) + 1;
  2441.  
  2442.     Length += 2;
  2443.  
  2444.     if ((Output = AllocateMemoryCell (Length)) == (char *)NULL)
  2445.     return (char *)NULL;
  2446.  
  2447. /* Build the new string */
  2448.  
  2449.     i = 0;
  2450.     sp = Output;
  2451.  
  2452. /* Build the string */
  2453.  
  2454.     while ((cp = Array[i++]) != NOWORD)
  2455.     {
  2456.     while ((*sp = *(cp++)))
  2457.         ++sp;
  2458.  
  2459.     if (!sep || (Array[i] != NOWORD))
  2460.         *(sp++) = sep;
  2461.     }
  2462.  
  2463.     *sp = 0;
  2464.     return Output;
  2465. }
  2466.  
  2467.  
  2468. /*
  2469.  * List of default known extensions and their type
  2470.  */
  2471.  
  2472. #define MAX_DEFAULT_EXTENSIONS    ARRAY_SIZE (DefaultExtensions)
  2473.  
  2474. static struct ExtType {
  2475.     char    *Extension;
  2476.     char    Type;
  2477. } DefaultExtensions [] = {
  2478.     { null,        EXTENSION_SHELL_SCRIPT },
  2479.     { SHELLExtension,    EXTENSION_SHELL_SCRIPT },
  2480.     { KSHELLExtension,    EXTENSION_SHELL_SCRIPT },
  2481. #if (OS_TYPE != OS_UNIX)
  2482.     { BATExtension,    EXTENSION_BATCH },
  2483.     { EXEExtension,    EXTENSION_EXECUTABLE },
  2484.     { COMExtension,    EXTENSION_EXECUTABLE },
  2485. #endif
  2486. };
  2487.  
  2488. /*
  2489.  * Built the list of valid extensions
  2490.  */
  2491.  
  2492. void    BuildExtensionLists (void)
  2493. {
  2494.     Word_B    *DList = (Word_B *)NULL;
  2495.     Word_B    *FList = (Word_B *)NULL;
  2496.     int        i;
  2497.     char    *pe;
  2498.     char    *sp;
  2499.     void        (*save_signal)(int);
  2500.  
  2501. /* Disable signals */
  2502.  
  2503.     save_signal = signal (SIGINT, SIG_IGN);
  2504.  
  2505. /* Release Old memory */
  2506.  
  2507.     if (ExecuteFunctionList != (char **)NULL)
  2508.     {
  2509.     ReleaseMemoryCell (*ExecuteFunctionList);
  2510.     ReleaseMemoryCell (ExecuteFunctionList);
  2511.     }
  2512.  
  2513.     if (ExecutableList != (char **)NULL)
  2514.     ReleaseMemoryCell (ExecutableList);
  2515.  
  2516. /* No extensions ? */
  2517.  
  2518.     if ((pe = GetVariableAsString (PathExtsLiteral, FALSE)) == null)
  2519.     {
  2520.     for (i = 0; i < MAX_DEFAULT_EXTENSIONS; i++)
  2521.         DList = AddWordToBlock (DefaultExtensions [i].Extension, DList);
  2522.  
  2523.     ExecutableList = GetWordList (AddWordToBlock ((char *)NULL, DList));
  2524.     ExecuteFunctionList = (char **)NULL;
  2525.  
  2526. /* Restore signals */
  2527.  
  2528.     signal (SIGINT, save_signal);
  2529.  
  2530.     return;
  2531.     }
  2532.  
  2533. /* OK - scan */
  2534.  
  2535.     pe = StringSave (pe);
  2536.  
  2537. /* Split out on semi-colons */
  2538.  
  2539.     do
  2540.     {
  2541.     if ((sp = strchr (pe, ';')) != (char *)NULL)
  2542.         *(sp++) = 0;
  2543.  
  2544.     FList = AddWordToBlock (pe, FList);
  2545.  
  2546. /* Build the new order of default extensions */
  2547.  
  2548.     for (i = 0; i < MAX_DEFAULT_EXTENSIONS; i++)
  2549.     {
  2550.         if (!NOCASE_COMPARE (pe, DefaultExtensions [i].Extension))
  2551.         {
  2552.         DList = AddWordToBlock (pe, DList);
  2553.         break;
  2554.         }
  2555.     }
  2556.  
  2557.     pe = sp;
  2558.     } while (pe != (char *)NULL);
  2559.  
  2560. /* Set up the two lists */
  2561.  
  2562.     ExecutableList = GetWordList (AddWordToBlock ((char *)NULL, DList));
  2563.     ExecuteFunctionList = GetWordList (AddWordToBlock ((char *)NULL, FList));
  2564.  
  2565. /* Restore signals */
  2566.  
  2567.     signal (SIGINT, save_signal);
  2568. }
  2569.  
  2570. /*
  2571.  * Find the location of an executable and return it's full path
  2572.  * name
  2573.  */
  2574.  
  2575. int    FindLocationOfExecutable (char *FullPath,
  2576.                   char *name)
  2577. {
  2578.     return ExtensionType (FindFileAndExtension (FullPath, name,
  2579.                         ExecutableList));
  2580. }
  2581.  
  2582. /*
  2583.  * Return the extension type for an extension
  2584.  */
  2585.  
  2586. static int F_LOCAL ExtensionType (char *ext)
  2587. {
  2588.     int        i;
  2589.  
  2590.     if (ext == (char *)NULL)
  2591.     return EXTENSION_NOT_FOUND;
  2592.     
  2593.     for (i = 0; i < MAX_DEFAULT_EXTENSIONS; i++)
  2594.     {
  2595.     if (!NOCASE_COMPARE (ext, DefaultExtensions [i].Extension))
  2596.         return (int)(DefaultExtensions [i].Type);
  2597.     }
  2598.  
  2599.     return EXTENSION_OTHER;
  2600. }
  2601.  
  2602. /*
  2603.  * Search the path for a file with the appropriate extension
  2604.  *
  2605.  * Returns a pointer to the extension.
  2606.  */
  2607.  
  2608. static char * F_LOCAL FindFileAndExtension (char *FullPath,
  2609.                         char *name,
  2610.                         char **Extensions)
  2611. {
  2612.     char    *sp;            /* Path pointers    */
  2613.     char    *ep;
  2614.     char    *xp;            /* In file name pointers */
  2615.     char    *xp1;
  2616.     int        i, fp;
  2617.     int        pathlen;
  2618.  
  2619. /* No extensions - no file */
  2620.  
  2621.     if (Extensions == (char **)NULL)
  2622.     {
  2623.     errno = ENOENT;
  2624.     return (char *)NULL;
  2625.     }
  2626.  
  2627. /* Scan the path for an executable */
  2628.  
  2629.     sp = ((FindPathCharacter (name) != (char *)NULL) ||
  2630.       (IsDriveCharacter (*(name + 1))))
  2631.           ? null
  2632.           : GetVariableAsString (PathLiteral, FALSE);
  2633.  
  2634.     do
  2635.     {
  2636.     sp = BuildNextFullPathName (sp, name, FullPath);
  2637.     ep = &FullPath[pathlen = strlen (FullPath)];
  2638.  
  2639. /* Get start of file name */
  2640.  
  2641.     if ((xp1 = FindLastPathCharacter (FullPath)) == (char *)NULL)
  2642.         xp1 = FullPath;
  2643.  
  2644.     else
  2645.         ++xp1;
  2646.  
  2647. /* Look up all 5 types */
  2648.  
  2649.     for (i = 0; Extensions[i] != (char *)NULL; i++)
  2650.     {
  2651.         if (pathlen + strlen (Extensions[i]) > (size_t)(FFNAME_MAX - 1))
  2652.             continue;
  2653.  
  2654.         strcpy (ep, Extensions[i]);
  2655.  
  2656.         if (S_access (FullPath, F_OK))
  2657.         {
  2658.  
  2659. /* If no extension, .ksh or .sh extension, check for shell script */
  2660.  
  2661.         if (((xp = strrchr (xp1, CHAR_PERIOD)) == (char *)NULL) ||
  2662.             (NOCASE_COMPARE (xp, SHELLExtension) == 0) ||
  2663.             (NOCASE_COMPARE (xp, KSHELLExtension) == 0))
  2664.         {
  2665.             if ((fp = CheckForScriptFile (FullPath, (char **)NULL,
  2666.                           (int *)NULL)) < 0)
  2667.             continue;
  2668.  
  2669.             S_close (fp, TRUE);
  2670.             return (xp == (char *)NULL) ? null : xp;
  2671.         }
  2672.  
  2673.         return xp;
  2674.         }
  2675.     }
  2676.     } while (sp != (char *)NULL);
  2677.  
  2678. /* Not found */
  2679.  
  2680.     errno = ENOENT;
  2681.     return (char *)NULL;
  2682. }
  2683.  
  2684. /*
  2685.  * Execute a script file
  2686.  */
  2687.  
  2688. static int F_LOCAL ExecuteScriptFile (char *Fullpath,
  2689.                       char **argv,
  2690.                       char **envp,
  2691.                       int  ForkAction,
  2692.                       bool ShellScript)
  2693. {
  2694.     int        res;            /* Result        */
  2695.     char    *params;        /* Script parameters    */
  2696.     int        nargc = 0;        /* # script args    */
  2697.     Word_B    *wb = (Word_B *)NULL;
  2698. #if (OS_TYPE == OS_DOS)
  2699.     union REGS    r;
  2700. #endif
  2701.  
  2702. /* Batfile - convert to DOS Format file name */
  2703.  
  2704.     if (!ShellScript)
  2705.     {
  2706.     PATH_TO_DOS (Fullpath);
  2707.     nargc = 0;
  2708.     }
  2709.  
  2710.     else if ((res = OpenForExecution (Fullpath, ¶ms, &nargc)) >= 0)
  2711.     S_close (res, TRUE);
  2712.  
  2713.     else
  2714.     {
  2715.     errno = ENOENT;
  2716.     return -1;
  2717.     }
  2718.  
  2719. /* If BAT file, use command.com else use sh */
  2720.  
  2721.     if (!ShellScript)
  2722.     {
  2723.     if (!SetUpCLI (GetVariableAsString (ComspecVariable, FALSE), &wb))
  2724.         return -1;
  2725.     
  2726.     wb = AddWordToBlock ("/c", wb);
  2727.  
  2728. /* Get the switch character */
  2729.  
  2730. #if (OS_TYPE == OS_DOS) && (OS_SIZE == OS_16)
  2731.     r.x.REG_AX = 0x3700;
  2732.     DosInterrupt (&r, &r);
  2733.  
  2734.     if ((r.h.al == 0) && (_osmajor < 4))
  2735.         *(wb->w_words[wb->w_nword - 1]) = (char)(r.h.dl);
  2736. #endif
  2737.     }
  2738.  
  2739. /* Stick in the pre-fix arguments */
  2740.  
  2741.     else if (nargc)
  2742.     wb = SplitString (params, wb);
  2743.  
  2744.     else if (params != null)
  2745.     wb = AddWordToBlock (params, wb);
  2746.  
  2747.     else
  2748.     wb = AddWordToBlock (GetVariableAsString (ShellVariableName, FALSE),
  2749.                  wb);
  2750.  
  2751. /* Add the rest of the parameters, and execute */
  2752.  
  2753.     res = ExecuteSpecialProcessor (Fullpath, argv, envp, ForkAction, wb);
  2754.  
  2755. /* Release allocated space */
  2756.  
  2757.     if (params != null)
  2758.     ReleaseMemoryCell ((void *)params);
  2759.  
  2760.     return res;
  2761. }
  2762.  
  2763. /*
  2764.  * Convert errno to error message on execute
  2765.  */
  2766.  
  2767. static char * F_LOCAL ConvertErrorNumber (void)
  2768. {
  2769.     switch (errno)
  2770.     {
  2771.     case ENOMEM:
  2772.         return strerror (ENOMEM);
  2773.  
  2774.     case ENOEXEC:
  2775.         return "program corrupt";
  2776.  
  2777.     case E2BIG:
  2778.         return AE2big;
  2779.  
  2780.     case ENOENT:
  2781.         return NotFound;
  2782.  
  2783.     case 0:
  2784.         return "No Shell";
  2785.     }
  2786.  
  2787.     return "cannot execute";
  2788. }
  2789.  
  2790. /*
  2791.  * Swap to disk error
  2792.  */
  2793.  
  2794. #ifdef OS_SWAPPING
  2795. static int F_LOCAL SwapToDiskError (int error, char *ErrorMessage)
  2796. {
  2797.  
  2798. /* Clean up */
  2799.  
  2800.     ClearSwapFile ();
  2801.     Swap_Mode &= (~SWAP_DISK);
  2802.     PrintErrorMessage (ErrorMessage);
  2803.     errno = error;
  2804.     return SetCommandReturnStatus (-1);
  2805. }
  2806.  
  2807. /*
  2808.  * Swap to memory
  2809.  */
  2810.  
  2811. static int F_LOCAL    SwapToMemory (int mode, char **envp)
  2812. {
  2813.     int        res;
  2814.     int        cr;
  2815.  
  2816. /* Swap and close memory handler */
  2817.  
  2818.     res = SpawnProcess (envp);
  2819.  
  2820.     cr = (SW_Mode != 3) ? XMS_Close () : EMS_Close ();
  2821.  
  2822.     if ((res != -2) && cr)        /* Report Close error ?        */
  2823.     {
  2824.     res = -2;
  2825.     errno = cr;
  2826.     }
  2827.  
  2828.     if (res == -2)
  2829.     (SW_Mode != 3) ? XMS_error (SwapFailed, errno)
  2830.                : EMS_error (SwapFailed, errno);
  2831.  
  2832.     else
  2833.     {
  2834.     ClearExtendedLineFile ();
  2835.     return SetCommandReturnStatus (res);
  2836.     }
  2837.  
  2838. /* Failed - disabled */
  2839.  
  2840.     Swap_Mode &= (~mode);
  2841.     return res;
  2842. }
  2843. #endif
  2844.  
  2845. /*
  2846.  * Check the program type
  2847.  */
  2848.  
  2849. void CheckProgramMode (char *Pname, ExeMode *PMode)
  2850. {
  2851.     char        *sp, *sp1;        /* Line pointers    */
  2852.     int            nFields;
  2853.     char        *SPname;
  2854.     int            builtin;        /* Builtin function    */
  2855.     LineFields        LF;
  2856.     long        value;
  2857.  
  2858. /* Check for internal no-globbed commands */
  2859.  
  2860.     if ((IsCommandBuiltIn (Pname, &builtin) != (int (*)(int, char **))NULL) &&
  2861.     ((builtin & BLT_SKIPGLOB) == BLT_SKIPGLOB))
  2862.     {
  2863.     PMode->Flags = EP_NOEXPAND | ((builtin & BLT_NOWORDS) ? EP_NOWORDS : 0);
  2864.     return;
  2865.     }
  2866.  
  2867. /* Set not found */
  2868.  
  2869.     PMode->Flags = EP_NONE;
  2870.  
  2871. /* Check not a function */
  2872.  
  2873.     if ((Pname == (char *)NULL) ||
  2874.     ((sp = GetVariableAsString ("EXTENDED_LINE", FALSE)) == null))
  2875.         return;
  2876.  
  2877. /* Get some memory for the input line and the file name */
  2878.  
  2879.     sp1 = ((sp1 = FindLastPathCharacter (Pname)) == (char *)NULL)
  2880.          ? Pname : sp1 + 1;
  2881.  
  2882.     if (IsDriveCharacter (*(sp1 + 1)))
  2883.     sp1 += 2;
  2884.  
  2885.     if ((SPname = StringCopy (sp1)) == null)
  2886.         return;
  2887.  
  2888.     if ((LF.Line = AllocateMemoryCell (LF.LineLength = 200)) == (char *)NULL)
  2889.     {
  2890.     ReleaseMemoryCell ((void *)SPname);
  2891.     return;
  2892.     }
  2893.  
  2894. /* Remove terminating .exe etc */
  2895.  
  2896.     if ((sp1 = strrchr (SPname, CHAR_PERIOD)) != (char *)NULL)
  2897.         *sp1 = 0;
  2898.  
  2899. /* Open the file */
  2900.  
  2901.     if ((LF.FP = FOpenFile (sp, sOpenReadMode)) == (FILE *)NULL)
  2902.     {
  2903.     ReleaseMemoryCell ((void *)LF.Line);
  2904.     ReleaseMemoryCell ((void *)SPname);
  2905.     return;
  2906.     }
  2907.  
  2908. /* Initialise the internal buffer */
  2909.  
  2910.     LF.Fields = (Word_B *)NULL;
  2911.  
  2912. /* Scan for the file name */
  2913.  
  2914.     while ((nFields = ExtractFieldsFromLine (&LF)) != -1)
  2915.     {
  2916.         if (nFields < 2)
  2917.             continue;
  2918.  
  2919. /* Remove terminating .exe etc */
  2920.  
  2921. #if (OS_TYPE != OS_UNIX)
  2922.     if ((sp = strrchr (LF.Fields->w_words[0], CHAR_PERIOD)) != (char *)NULL)
  2923.         *sp = 0;
  2924. #endif
  2925.  
  2926.         if (NOCASE_COMPARE (LF.Fields->w_words[0], SPname))
  2927.             continue;
  2928.  
  2929. /* What type? */
  2930.  
  2931.     if (NOCASE_COMPARE (LF.Fields->w_words[1], "unix") == 0)
  2932.         PMode->Flags = (unsigned int )(EP_UNIXMODE |
  2933.                 CheckForCommonOptions (&LF, 2));
  2934.  
  2935.     else if (NOCASE_COMPARE (LF.Fields->w_words[1], LIT_dos) == 0)
  2936.         PMode->Flags = (unsigned int )(EP_DOSMODE |
  2937.                 CheckForCommonOptions (&LF, 2));
  2938.  
  2939. /* Must have a valid name and we can get memory for it */
  2940.  
  2941.     else if ((NOCASE_COMPARE (LF.Fields->w_words[1], "environ") == 0) &&
  2942.          (nFields >= 3) &&
  2943.          (!IsValidVariableName (LF.Fields->w_words[2])) &&
  2944.          ((PMode->Name =
  2945.              StringCopy (LF.Fields->w_words[2])) != null))
  2946.     {
  2947.         PMode->Flags = EP_ENVIRON;
  2948.         PMode->FieldSep = 0;
  2949.  
  2950.         if ((nFields >= 4) &&
  2951.         ConvertNumericValue (LF.Fields->w_words[3], &value, 0))
  2952.         PMode->FieldSep = (unsigned char)value;
  2953.  
  2954.         if (!PMode->FieldSep)
  2955.         PMode->FieldSep = CHAR_SPACE;
  2956.     }
  2957.  
  2958.     else
  2959.         PMode->Flags = CheckForCommonOptions (&LF, 1);
  2960.  
  2961.         break;
  2962.     }
  2963.  
  2964.     CloseFile (LF.FP);
  2965.     ReleaseMemoryCell ((void *)LF.Line);
  2966.     ReleaseMemoryCell ((void *)SPname);
  2967. }
  2968.  
  2969. /*
  2970.  * Check for common fields
  2971.  */
  2972.  
  2973. static unsigned int F_LOCAL CheckForCommonOptions (LineFields *LF, int Start)
  2974. {
  2975.     unsigned int    Flags = 0;
  2976.     int            i, j;
  2977.  
  2978.     if (LF->Fields == (Word_B *)NULL)
  2979.     return 0;
  2980.  
  2981.     for (i = Start; i < LF->Fields->w_nword; i++)
  2982.     {
  2983.     for (j = 0; j < COMMON_FIELD_COUNT; ++j)
  2984.     {
  2985.         if (!NOCASE_COMPARE (LF->Fields->w_words[i], CommonFields[j].Name))
  2986.         {
  2987.         Flags |= CommonFields[j].Flag;
  2988.         break;
  2989.         }
  2990.     }
  2991.     }
  2992.  
  2993.     return Flags;
  2994. }
  2995.  
  2996. /*
  2997.  * Convert UNIX format lines to DOS format if appropriate.
  2998.  * Build Environment variable for some programs.
  2999.  */
  3000.  
  3001. static int F_LOCAL EnvironExecute (char **argv, int ForkAction)
  3002. {
  3003.     int        s_errno;
  3004.     int        RetVal = 1;
  3005.     char    *NewArgs[3];
  3006.     char    *cp;
  3007.  
  3008. /* If this command does not pass the command string in the environment,
  3009.  * no action required
  3010.  */
  3011.  
  3012.     if (ExecProcessingMode.Flags != EP_ENVIRON)
  3013.         return 0;
  3014.  
  3015.     if ((cp = BuildOS2String (&argv[1], ExecProcessingMode.FieldSep))
  3016.          == (char *)NULL)
  3017.     {
  3018.     ExecProcessingMode.Flags = EP_NONE;
  3019.         return 0;
  3020.     }
  3021.  
  3022.     SetVariableFromString (ExecProcessingMode.Name, cp);
  3023.     SetVariableStatus (ExecProcessingMode.Name, STATUS_EXPORT);
  3024.  
  3025. /* Build and execute the environment */
  3026.  
  3027.     NewArgs[0] = argv[0];
  3028.     NewArgs[1] = ExecProcessingMode.Name;
  3029.     NewArgs[2] = (char *)NULL;
  3030.  
  3031.     RetVal = LocalExecve (NewArgs, BuildCommandEnvironment (), ForkAction);
  3032.     s_errno = errno;
  3033.     UnSetVariable (ExecProcessingMode.Name, -1, FALSE);
  3034.     errno = s_errno;
  3035.     return RetVal;
  3036. }
  3037.  
  3038. /*
  3039.  * Set Interrupt handling vectors - moved from sh0.asm
  3040.  */
  3041.  
  3042. #ifdef OS_SWAPPING
  3043. static int F_LOCAL    SpawnProcess (char **envp)
  3044. {
  3045.     void    (interrupt far *SW_I00_V) (void);    /* Int 00 address */
  3046.     void    (interrupt far *SW_I23_V) (void);    /* Int 23 address*/
  3047.     int            res;
  3048. #if 0
  3049.     union REGS        r;
  3050.     unsigned char    Save;
  3051.  
  3052.     r.x.REG_AX = 0x3300;
  3053.     DosInterrupt (&r, &r);
  3054.     Save = r.h.al;
  3055.     fprintf (stderr, "Break Status: %s (%u)\n", Save ? "on" : "off", Save);
  3056.  
  3057.     r.x.REG_AX = 0x3301;
  3058.     r.h.dl = 1;
  3059.     DosInterrupt (&r, &r);
  3060.     fprintf (stderr, "Break Status: %s (%u)\n", r.h.al ? "on" : "off", r.h.al);
  3061. #endif
  3062.  
  3063. /*
  3064.  * Save current vectors
  3065.  */
  3066.  
  3067.     SW_I00_V = GetInterruptVector (0x00);
  3068.     SW_I23_V = GetInterruptVector (0x23);
  3069.  
  3070. /*
  3071.  * Set In shell flag for Interrupt 23, and set to new interrupts
  3072.  */
  3073.  
  3074.     SW_I23_InShell = 0;
  3075.  
  3076.     SetInterruptVector (0x23, SW_Int23);
  3077.     SetInterruptVector (0x00, SW_Int00);
  3078.  
  3079.     res = SA_spawn (envp);
  3080.  
  3081. /*
  3082.  * Restore interrupt vectors
  3083.  */
  3084.  
  3085.     SetInterruptVector (0x00, SW_I00_V);
  3086.     SetInterruptVector (0x23, SW_I23_V);
  3087.  
  3088. #if 0
  3089.     r.x.REG_AX = 0x3300;
  3090.     DosInterrupt (&r, &r);
  3091.     fprintf (stderr, "Break Status: %s (%u)\n", r.h.al ? "on" : "off", r.h.al);
  3092.     r.x.REG_AX = 0x3301;
  3093.     r.h.dl = Save;
  3094.     DosInterrupt (&r, &r);
  3095. #endif
  3096.  
  3097. /*
  3098.  * Check for an interrupt
  3099.  */
  3100.  
  3101.     if (SW_intr)
  3102.     raise (SIGINT);
  3103.  
  3104.     return res;
  3105. }
  3106. #endif
  3107.  
  3108. /*
  3109.  * Check Parameter line length
  3110.  *
  3111.  * Under OS2, we don't build the command line.  Just check it.
  3112.  */
  3113.  
  3114. static bool F_LOCAL CheckParameterLength (char **argv)
  3115. {
  3116.     int        CmdLineLength;
  3117.     char    *CommandLine;
  3118.     bool    RetVal;
  3119.     char    **SavedArgs = argv;
  3120.  
  3121. /* Check for special case.  If there are any special characters and we can
  3122.  * use UNIX mode, use it
  3123.  */
  3124.  
  3125.     if (ExecProcessingMode.Flags & EP_UNIXMODE)
  3126.     {
  3127.     while (*argv != (char *)NULL)
  3128.     {
  3129.         if (strpbrk (*(argv++), WildCards) != (char *)NULL)
  3130.         return FALSE;
  3131.     }
  3132.     }
  3133.  
  3134. /*
  3135.  * Do any parameter conversion - adding quotes or backslashes, but don't
  3136.  * update argv.
  3137.  */
  3138.  
  3139.     CmdLineLength = CountNumberArguments (argv = SavedArgs);
  3140.  
  3141.     if ((SavedArgs = (char **)
  3142.         AllocateMemoryCell ((CmdLineLength + 1) * sizeof (char *)))
  3143.         == (char **)NULL)
  3144.     return FALSE;
  3145.  
  3146. /* Save a copy of the argument addresses */
  3147.  
  3148.     memcpy (SavedArgs, argv, (CmdLineLength + 1) * sizeof (char *));
  3149.  
  3150. /* Build the command line */
  3151.  
  3152.     if ((CommandLine = BuildOS2String (ProcessSpaceInParameters (SavedArgs),
  3153.                         CHAR_SPACE)) == (char *)NULL)
  3154.     {
  3155.     ReleaseMemoryCell (SavedArgs);
  3156.     return FALSE;
  3157.     }
  3158.  
  3159. /*
  3160.  * Check command line length. Under DOS, this is simple.  On OS/2, we have
  3161.  * to remember that we default to the EMX interface, which requires
  3162.  * twice as much space, plus some nulls.  Also remember start DOS programs
  3163.  * on OS/2 or NT have restricted space.
  3164.  */
  3165.  
  3166.     CmdLineLength = strlen (CommandLine);
  3167.  
  3168.     if ((ApplicationType & EXETYPE_DOS) &&
  3169.     (CmdLineLength >= DOS_CMD_LINE_MAX - 2))
  3170.     {
  3171.     errno = E2BIG;
  3172.     RetVal = FALSE;
  3173.     }
  3174.  
  3175. #if (OS_TYPE == OS_OS2)
  3176.     else if (CmdLineLength >= (((CMD_LINE_MAX - 2) / 2) +
  3177.                   CountNumberArguments (SavedArgs) + 3))
  3178.     {
  3179.     errno = E2BIG;
  3180.     RetVal = FALSE;
  3181.     }
  3182.  
  3183. #elif (OS_TYPE == OS_NT) 
  3184.     else if (CmdLineLength >= CMD_LINE_MAX - 2)
  3185.     {
  3186.     errno = E2BIG;
  3187.     RetVal = FALSE;
  3188.     }
  3189. #endif
  3190.  
  3191. /* Terminate the line */
  3192.  
  3193.     else
  3194.     {
  3195. #ifdef OS_SWAPPING
  3196.     strcpy (cmd_line + 1, CommandLine);
  3197.     cmd_line[CmdLineLength + 1] = CHAR_RETURN;
  3198.     cmd_line[0] = (char)CmdLineLength;
  3199. #endif
  3200.     RetVal = TRUE;
  3201.     }
  3202.  
  3203.     ReleaseMemoryCell (SavedArgs);
  3204.     ReleaseMemoryCell (CommandLine);
  3205.  
  3206.     return RetVal;
  3207. }
  3208.  
  3209. /*
  3210.  * Convert any parameters with spaces in the to start and end with double
  3211.  * quotes.
  3212.  *
  3213.  * Under OS2, the old string is NOT released.
  3214.  */
  3215.  
  3216. static char ** F_LOCAL ProcessSpaceInParameters (char **argv)
  3217. {
  3218.     char    **Start = argv;
  3219.     char    *new;
  3220.     char    *cp;
  3221.     char    *sp;
  3222.     int        Count;
  3223.  
  3224. /* If noquote set, don't even try */
  3225.  
  3226.     if (ExecProcessingMode.Flags & EP_NOQUOTE)
  3227.     return Start;
  3228.  
  3229. /* Protect parameters with TABS */
  3230.  
  3231.     while (*argv != (char *)NULL)
  3232.     {
  3233.         if ((strpbrk (*argv, " \t") != (char *)NULL) ||
  3234.         (strlen (*argv) == 0) ||
  3235.         ((ExecProcessingMode.Flags & EP_QUOTEWILD) &&
  3236.          (strpbrk (*argv, WildCards) != (char *)NULL)))
  3237.     {
  3238.  
  3239. /* Count number of Double quotes in the parameter */
  3240.  
  3241.         Count = CountDoubleQuotes (*argv);
  3242.  
  3243. /* Get some memory - give up update if out of memory */
  3244.  
  3245.         if ((new = GetAllocatedSpace (strlen (*argv) + (Count * 2) +
  3246.                       3)) == (char *)NULL)
  3247.             return Start;
  3248.  
  3249.         SetMemoryAreaNumber ((void *)new,
  3250.                      GetMemoryAreaNumber ((void *)*argv));
  3251.         *new = CHAR_DOUBLE_QUOTE;
  3252.  
  3253. /* Escape any double quotes in the string */
  3254.  
  3255.         cp = *argv;
  3256.         sp = new + 1;
  3257.  
  3258.         while (*cp)
  3259.         {
  3260.         if (*cp == CHAR_DOUBLE_QUOTE)
  3261.         {
  3262.             *(sp++) = CHAR_META;
  3263.             *(sp++) = *(cp++);
  3264.         }
  3265.  
  3266.         else if (*cp != CHAR_META)
  3267.             *(sp++) = *(cp++);
  3268.  
  3269. /* Handle escapes - count them */
  3270.  
  3271.         else
  3272.         {
  3273.             *(sp++) = *(cp++);
  3274.  
  3275.             if (*cp == CHAR_DOUBLE_QUOTE)
  3276.             {
  3277.             *(sp++) = CHAR_META;
  3278.             *(sp++) = CHAR_META;
  3279.             }
  3280.  
  3281.             else if (*cp == 0)
  3282.             {
  3283.             *(sp++) = CHAR_META;
  3284.             break;
  3285.             }
  3286.  
  3287.             *(sp++) = *(cp++);
  3288.         }
  3289.         }
  3290.  
  3291. /* Append the terminating double quotes */
  3292.  
  3293.         strcpy (sp, DoubleQuotes);
  3294.         *argv = new;
  3295.     }
  3296.  
  3297. /* Check for any double quotes */
  3298.  
  3299.     else if ((Count = CountDoubleQuotes (*argv)))
  3300.     {
  3301.  
  3302. /* Got them - escape them */
  3303.  
  3304.         if ((new = GetAllocatedSpace (strlen (*argv) + Count + 1))
  3305.             == (char *)NULL)
  3306.             return Start;
  3307.  
  3308.         SetMemoryAreaNumber ((void *)new,
  3309.                      GetMemoryAreaNumber ((void *)*argv));
  3310.  
  3311. /* Copy the string, escaping DoubleQuotes */
  3312.  
  3313.         cp = *argv;
  3314.         sp = new;
  3315.  
  3316.         while ((*sp = *(cp++)))
  3317.         {
  3318.         if (*sp == CHAR_DOUBLE_QUOTE)
  3319.         {
  3320.             *(sp++) = CHAR_META;
  3321.             *sp = CHAR_DOUBLE_QUOTE;
  3322.         }
  3323.  
  3324.         sp++;
  3325.         }
  3326.  
  3327.         *argv = new;
  3328.     }
  3329.  
  3330. /* Next parameter */
  3331.  
  3332.     argv++;
  3333.     }
  3334.  
  3335.     return Start;
  3336. }
  3337.  
  3338. /*
  3339.  * Count DoubleQuotes
  3340.  */
  3341.  
  3342. static int F_LOCAL CountDoubleQuotes (char *string)
  3343. {
  3344.     int        Count = 0;
  3345.  
  3346.     while ((string = strchr (string, CHAR_DOUBLE_QUOTE)) != (char *)NULL)
  3347.     {
  3348.     Count++;
  3349.     string++;
  3350.     }
  3351.  
  3352.     return Count;
  3353. }
  3354.  
  3355. /*
  3356.  * Save and Restore the Parameters array ($1, $2 etc)
  3357.  */
  3358.  
  3359. static void F_LOCAL SaveNumericParameters (char **wp, SaveParameters *SaveArea)
  3360. {
  3361.     SaveArea->Array = ParameterArray;
  3362.     SaveArea->Count = ParameterCount;
  3363.  
  3364.     ParameterArray = wp;
  3365.     for (ParameterCount = 0; ParameterArray[ParameterCount] != NOWORD;
  3366.      ++ParameterCount)
  3367.     continue;
  3368.  
  3369.     SetVariableFromNumeric (ParameterCountVariable, (long)--ParameterCount);
  3370. }
  3371.  
  3372. static void F_LOCAL RestoreTheParameters (SaveParameters *SaveArea)
  3373. {
  3374.     ParameterArray = SaveArea->Array;
  3375.     ParameterCount = SaveArea->Count;
  3376.     SetVariableFromNumeric (ParameterCountVariable, (long)ParameterCount);
  3377. }
  3378.  
  3379. /*
  3380.  * Special OS/2 processing for execve and spawnve
  3381.  */
  3382.  
  3383. #if (OS_TYPE == OS_OS2)
  3384. static int F_LOCAL OS_DosExecProgram (int Mode, char *Program, char **argv,
  3385.                       char **envp)
  3386. {
  3387.     OSCALL_PARAM    fExecFlags;
  3388.     void        (*sig_int)();        /* Interrupt signal    */
  3389.     RESULTCODES        rescResults;
  3390.     PID            pidProcess;
  3391.     PID            pidWait;
  3392.     char        *OS2Environment;
  3393.     char        *OS2Arguments;
  3394.     char        **SavedArgs;
  3395.     int            argc;
  3396.  
  3397. /* Set the error module to null */
  3398.  
  3399.     *FailName = 0;
  3400.     OS_DosExecPgmReturnCode = 0;
  3401.  
  3402. /* Convert spawn mode to DosExecPgm mode */
  3403.  
  3404.     switch (Mode)
  3405.     {
  3406.     case P_WAIT:
  3407.     case P_NOWAIT:
  3408.         fExecFlags = EXEC_ASYNCRESULT;
  3409.         break;
  3410.  
  3411.     case P_NOWAITO:
  3412.     case OLD_P_OVERLAY:
  3413.         fExecFlags = EXEC_ASYNC;
  3414.         break;
  3415.  
  3416.     case P_DETACH:
  3417.         fExecFlags = EXEC_BACKGROUND;
  3418.         break;
  3419.     }
  3420.  
  3421. /* Build OS/2 argument string
  3422.  *
  3423.  * 1.  Count the number of arguments.
  3424.  * 2.  Add 2 for: 1 - NULL; 2 - argv[0]; 3 - the stringed arguments
  3425.  * 3.  save copy of arguments at offset 2.
  3426.  * 4.  On original argv, process white space and convert to OS2 Argument string.
  3427.  * 5.  Set up program name at offset 2 as ~argv
  3428.  * 6.  Convert zero length args to "~" and args beginning with ~ to ~~
  3429.  * 7.  Build OS2 Argument string (at last).
  3430.  */
  3431.  
  3432.     argc = CountNumberArguments (argv);
  3433.  
  3434.     if ((SavedArgs = (char **)AllocateMemoryCell ((argc + 3) * sizeof (char *)))
  3435.         == (char **)NULL)
  3436.     return -1;
  3437.  
  3438.     memcpy (SavedArgs + 2, argv, (argc + 1) * sizeof (char *));
  3439.  
  3440. /* Set program name at Offset 0 */
  3441.  
  3442.     SavedArgs[0] = *argv;
  3443.  
  3444. /* Build OS2 Argument string in Offset 1 */
  3445.  
  3446.     if ((SavedArgs[1] = BuildOS2String (ProcessSpaceInParameters (&argv[1]),
  3447.                     CHAR_SPACE)) == (char *)NULL)
  3448.     return -1;
  3449.  
  3450. /* Set up the new arg 2 - ~ + programname */
  3451.  
  3452.     if ((SavedArgs[2] = InsertCharacterAtStart (*argv)) == (char *)NULL)
  3453.     return -1;
  3454.  
  3455. /* Convert zero length args and args starting with a ~ */
  3456.  
  3457.     for (argc = 3; SavedArgs[argc] != NOWORD; argc++)
  3458.     {
  3459.     if (strlen (SavedArgs[argc]) == 0)
  3460.         SavedArgs[argc] = "~";
  3461.  
  3462.     else if ((*SavedArgs[argc] == CHAR_TILDE) &&
  3463.          ((SavedArgs[argc] = InsertCharacterAtStart (SavedArgs[argc]))
  3464.             == (char *)NULL))
  3465.         return -1;
  3466.     }
  3467.  
  3468. /* Build the full argument list */
  3469.  
  3470.     if ((OS2Arguments = BuildOS2String (SavedArgs, 0)) == (char *)NULL)
  3471.     return -1;
  3472.  
  3473. /* Build OS/2 environment string */
  3474.  
  3475.     if ((OS2Environment = BuildOS2String (envp, 0)) == (char *)NULL)
  3476.     return -1;
  3477.  
  3478. /* Change signal handling */
  3479.  
  3480.     if (fExecFlags == EXEC_ASYNCRESULT)
  3481.     {
  3482.     char    *cp;
  3483.  
  3484. /* Also change the window title to match the foreground program */
  3485.  
  3486.     if ((cp = strrchr (Program, CHAR_DOS_PATH)) == (char *)NULL)
  3487.         cp = Program;
  3488.  
  3489.     else
  3490.         cp++;
  3491.  
  3492.     strcpy (FailName, cp);
  3493.  
  3494.     if ((cp = strrchr (FailName, CHAR_PERIOD)) != (char *)NULL)
  3495.         *cp = 0;
  3496.  
  3497.     SetWindowName (FailName);
  3498.     *FailName = 0;
  3499.  
  3500.     IgnoreInterrupts = TRUE;
  3501.     sig_int = signal (SIGINT, SIG_IGN);
  3502.     }
  3503.  
  3504. /* Exec it - with hard-error processing turned off */
  3505.  
  3506.     DISABLE_HARD_ERRORS;
  3507.  
  3508.     OS_DosExecPgmReturnCode = DosExecPgm (FailName, sizeof (FailName),
  3509.                           fExecFlags, OS2Arguments,
  3510.                       OS2Environment, &rescResults,
  3511.                       Program);
  3512.  
  3513.     ENABLE_HARD_ERRORS;
  3514.  
  3515.     if (fExecFlags == EXEC_ASYNCRESULT)
  3516.     {
  3517.     signal (SIGINT, SIG_IGN);
  3518.  
  3519. /* If the process started OK, wait for it */
  3520.  
  3521.     if (((OS_DosExecPgmReturnCode == NO_ERROR) ||
  3522.          (OS_DosExecPgmReturnCode == ERROR_INTERRUPT)) &&
  3523.         (Mode == P_WAIT))
  3524.     {
  3525.         pidWait = rescResults.codeTerminate;
  3526.  
  3527. /* Re-try on interrupted system calls - and kill the child.  Why, because
  3528.  * sometimes the kill does not go to the child
  3529.  */
  3530.  
  3531.         while ((OS_DosExecPgmReturnCode = DosCwait (DCWA_PROCESSTREE,
  3532.                                 DCWW_WAIT,
  3533.                             &rescResults,
  3534.                             &pidProcess,
  3535.                             pidWait))
  3536.                          == ERROR_INTERRUPT)
  3537.         DosKillProcess (DKP_PROCESS, pidWait);
  3538.     }
  3539.  
  3540.     IgnoreInterrupts = FALSE;
  3541.     signal (SIGINT, sig_int);
  3542.     }
  3543.  
  3544.  
  3545. /*
  3546.  * What happened ?.  OS/2 Error - Map to UNIX errno.  Why can't people
  3547.  * write libraries right??  Or provide access at a high level.  We could
  3548.  * call _dosret if the interface did not require me to write more
  3549.  * assembler.
  3550.  */
  3551.  
  3552.     if (OS_DosExecPgmReturnCode != 0)
  3553.     {
  3554.     switch (OS_DosExecPgmReturnCode)
  3555.     {
  3556. #ifdef EAGAIN
  3557.         case ERROR_NO_PROC_SLOTS:
  3558.         errno = EAGAIN;
  3559.         return -1;
  3560. #endif
  3561.  
  3562.         case ERROR_NOT_ENOUGH_MEMORY:
  3563.         errno = ENOMEM;
  3564.         return -1;
  3565.  
  3566.         case ERROR_ACCESS_DENIED:
  3567.         case ERROR_DRIVE_LOCKED:
  3568.         case ERROR_LOCK_VIOLATION:
  3569.         case ERROR_SHARING_VIOLATION:
  3570.         errno = EACCES;
  3571.         return -1;
  3572.  
  3573.         case ERROR_FILE_NOT_FOUND:
  3574.         case ERROR_PATH_NOT_FOUND:
  3575.         case ERROR_PROC_NOT_FOUND:
  3576.         errno = ENOENT;
  3577.         return -1;
  3578.  
  3579.         case ERROR_BAD_ENVIRONMENT:
  3580.         case ERROR_INVALID_DATA:
  3581.         case ERROR_INVALID_FUNCTION:
  3582.         case ERROR_INVALID_ORDINAL:
  3583.         case ERROR_INVALID_SEGMENT_NUMBER:
  3584.         case ERROR_INVALID_STACKSEG:
  3585.         case ERROR_INVALID_STARTING_CODESEG:
  3586.         errno = EINVAL;
  3587.         return -1;
  3588.  
  3589.         case ERROR_TOO_MANY_OPEN_FILES:
  3590.         errno = EMFILE;
  3591.         return -1;
  3592.  
  3593.         case ERROR_INTERRUPT:
  3594.         case ERROR_NOT_DOS_DISK:
  3595.         case ERROR_SHARING_BUFFER_EXCEEDED:
  3596.         errno = EIO;
  3597.         return -1;
  3598.  
  3599.         case ERROR_BAD_ARGUMENTS:
  3600.         errno = E2BIG;
  3601.         return -1;
  3602.  
  3603.         default:
  3604.         errno = ENOEXEC;
  3605.         return -1;
  3606.     }
  3607.     }
  3608.  
  3609. /* If exec - exit */
  3610.  
  3611.     switch (Mode)
  3612.     {
  3613.     case OLD_P_OVERLAY:            /* exec - exit at once */
  3614.         FinalExitCleanUp (0);
  3615.  
  3616.     case P_WAIT:                /* Get exit code    */
  3617.         return rescResults.codeResult;
  3618.  
  3619.     case P_NOWAIT:                /* Get PID or SID    */
  3620.     case P_NOWAITO:
  3621.     case P_DETACH:
  3622.         return rescResults.codeTerminate;
  3623.     }
  3624.  
  3625.     errno = EINVAL;
  3626.     return -1;
  3627. }
  3628.  
  3629. /*
  3630.  * Insert character at start of string
  3631.  *
  3632.  * Return NULL or new string
  3633.  */
  3634.  
  3635. static char    *InsertCharacterAtStart (char *string)
  3636. {
  3637.     char    *cp;
  3638.  
  3639.     if ((cp = (char *)AllocateMemoryCell (strlen (string) + 2)) == (char *)NULL)
  3640.     return (char *)NULL;
  3641.  
  3642.     *cp = CHAR_TILDE;
  3643.     strcpy (cp + 1, string);
  3644.  
  3645.     return cp;
  3646. }
  3647. #endif
  3648.  
  3649. /*
  3650.  * Special NT processing for execve and spawnve.  Not really sure what to
  3651.  * do here yet!
  3652.  */
  3653.  
  3654. #if (OS_TYPE == OS_NT)
  3655. static int F_LOCAL OS_DosExecProgram (int Mode, char *Program, char **argv,
  3656.                       char **envp)
  3657. {
  3658.     DWORD            dwLogicalDrives = GetLogicalDrives();
  3659.     char            szNewDrive[4];
  3660.     unsigned int        i;
  3661.     char            **new_envp;
  3662.     char            ldir[PATH_MAX + 6];
  3663.     char            *nt_ep;
  3664.     char            *temp;
  3665.     int                off = 0;
  3666.     STARTUPINFO            StartupInfo;
  3667.     PROCESS_INFORMATION        ProcessInformation;
  3668.     char            *Environment;
  3669.     char            *PgmInputs;
  3670.     BOOL            rv;
  3671.     DWORD            ExitCode;
  3672.  
  3673. /*
  3674.  * Set up NT directory variables
  3675.  */
  3676.  
  3677.     if ((new_envp = (char **)GetAllocatedSpace (GetMemoryCellSize (envp) +
  3678.                         sizeof (char *) * 26))
  3679.           == (char **)NULL)
  3680.     {
  3681.     errno = ENOMEM;
  3682.     return -1;
  3683.     }
  3684.  
  3685.     strcpy (szNewDrive, "x:");
  3686.  
  3687.     for (i = 0; i < 25; i++)
  3688.     {
  3689.     if (dwLogicalDrives & (1L << i))
  3690.     {
  3691.         szNewDrive[0] = i + 'A';
  3692.  
  3693. /* If the drive does not exist - give up */
  3694.  
  3695.         DISABLE_HARD_ERRORS;
  3696.         ExitCode = GetFullPathName (szNewDrive, PATH_MAX + 6, ldir, &temp);
  3697.         ENABLE_HARD_ERRORS;
  3698.  
  3699.         if (!ExitCode)
  3700.             continue;
  3701.  
  3702.         if ((nt_ep = GetAllocatedSpace (strlen (ldir) + 5)) == (char *)NULL)
  3703.         {
  3704.         errno = ENOMEM;
  3705.         return -1;
  3706.         }
  3707.  
  3708.         sprintf (nt_ep, "=%c:=%s", i + 'A', ldir);
  3709.         new_envp[off++] = nt_ep;
  3710.     }
  3711.     }
  3712.    
  3713.     memcpy (&new_envp[off], envp, GetMemoryCellSize (envp));
  3714.  
  3715. /* Setup environment block */
  3716.  
  3717.     if ((Environment = BuildOS2String (new_envp, 0)) == (char *)NULL)
  3718.     return -1;
  3719.  
  3720. /* Setup parameter block */
  3721.  
  3722.     ProcessSpaceInParameters (argv);
  3723.  
  3724.     argv[0] = Program;
  3725.  
  3726.     if ((PgmInputs = BuildOS2String (argv, CHAR_SPACE)) == (char *)NULL)
  3727.     return -1;
  3728.  
  3729. /* Set up startup info */
  3730.  
  3731.     memset (&StartupInfo, 0, sizeof (StartupInfo));
  3732.  
  3733.     StartupInfo.cb = sizeof (StartupInfo);
  3734.     StartupInfo.lpDesktop = NULL;
  3735.     StartupInfo.lpTitle = NULL;
  3736.     StartupInfo.dwFlags = 0;
  3737.  
  3738. /* Change Window name ? */
  3739.  
  3740.     if (Mode == P_WAIT)
  3741.     {
  3742.     char    *cp;
  3743.     char    *Name;
  3744.  
  3745. /* Also change the window title to match the foreground program */
  3746.  
  3747.     if ((cp = strrchr (Program, CHAR_DOS_PATH)) == (char *)NULL)
  3748.         cp = Program;
  3749.  
  3750.     else
  3751.         cp++;
  3752.  
  3753.     Name = StringCopy (cp);
  3754.  
  3755.     if ((cp = strrchr (Name, CHAR_PERIOD)) != (char *)NULL)
  3756.         *cp = 0;
  3757.  
  3758.     SetWindowName (Name);
  3759.     ReleaseMemoryCell (Name);
  3760.     }
  3761.  
  3762. /* Exec it - with hard-error processing turned off */
  3763.  
  3764.     DISABLE_HARD_ERRORS;
  3765.     rv = CreateProcess (Program, PgmInputs, NULL, NULL, TRUE,
  3766.             NORMAL_PRIORITY_CLASS,
  3767.             Environment, NULL, &StartupInfo, &ProcessInformation);
  3768. /*#define DETACHED_PROCESS            0x00000008*/
  3769.  
  3770.     ENABLE_HARD_ERRORS;
  3771.  
  3772.     if (!rv)
  3773.     {
  3774.     OS_DosExecPgmReturnCode = GetLastError ();
  3775.     errno = ENOENT;
  3776.     return -1;
  3777.     }
  3778.     
  3779.     if (Mode == P_WAIT)
  3780.     {
  3781.     if ((ExitCode = WaitForSingleObject (ProcessInformation.hProcess,
  3782.                          INFINITE)) == WAIT_OBJECT_0)
  3783.         return ExitCode;
  3784.  
  3785. /* Wait failed */
  3786.  
  3787.     OS_DosExecPgmReturnCode = GetLastError ();
  3788.     errno = EINVAL;
  3789.     return -1;
  3790.     }
  3791.  
  3792. /* Otherwise, return the process ID. */
  3793.  
  3794.     return ProcessInformation.dwProcessId;
  3795. }
  3796. #endif
  3797.  
  3798. /*
  3799.  * Execute a Function
  3800.  */
  3801.  
  3802. static bool F_LOCAL ExecuteFunction (char **wp, int *RetVal, bool CGVLCalled)
  3803. {
  3804.     Break_C            *s_RList = Return_List;
  3805.     Break_C            *s_BList = Break_List;
  3806.     Break_C            *s_SList = SShell_List;
  3807.     Break_C            BreakContinue;
  3808.     C_Op            *New;
  3809.     FunctionList        *s_CurrentFunction = CurrentFunction;
  3810.     SaveParameters        s_Parameters;
  3811.     FunctionList        *fop;
  3812.     GetoptsIndex        GetoptsSave;
  3813.     bool            TrapZeroExists;
  3814.     char            *Ext;
  3815.     char            *FullPath;
  3816.  
  3817.     TrapZeroExists = C2bool (GetVariableAsString ("~0", FALSE) != null);
  3818.  
  3819. /* Find the extension type - if it exists */
  3820.  
  3821.     FullPath = GetAllocatedSpace (FFNAME_MAX);
  3822.     
  3823. /* Check for a psuedo-function */
  3824.  
  3825.     switch (ExtensionType (Ext = FindFileAndExtension (FullPath,
  3826.                                wp[0],
  3827.                                ExecuteFunctionList)))
  3828.     {
  3829.     case EXTENSION_OTHER:
  3830.         if ((fop = LookUpFunction (Ext, TRUE)) == (FunctionList *)NULL)
  3831.         return FALSE;
  3832.  
  3833.         wp[0] = FullPath;
  3834.         break;
  3835.  
  3836. /* Common extension for all .sh, .ksh and "" scripts.  If we find one, use
  3837.  * it, otherwise, check for a function of this name
  3838.  */
  3839.  
  3840.     case EXTENSION_SHELL_SCRIPT:
  3841.         Ext = ".ksh";
  3842.  
  3843.     case EXTENSION_BATCH:
  3844.         if ((fop = LookUpFunction (Ext, TRUE)) != (FunctionList *)NULL)
  3845.         {
  3846.         wp[0] = FullPath;
  3847.         break;
  3848.         }
  3849.  
  3850. /* Check for a real function */
  3851.  
  3852.     case EXTENSION_NOT_FOUND:
  3853.     case EXTENSION_EXECUTABLE:
  3854.     default:
  3855.         if ((fop = LookUpFunction (wp[0], FALSE)) == (FunctionList *)NULL)
  3856.         return FALSE;
  3857.     }
  3858.  
  3859. /* Ok, we really have a function to execute.
  3860.  * Save the current variable list
  3861.  */
  3862.  
  3863.     if (!CGVLCalled && (CreateGlobalVariableList (FLAGS_FUNCTION) == -1))
  3864.     {
  3865.     *RetVal =  -1;
  3866.     return TRUE;
  3867.     }
  3868.  
  3869. /* Set up $0..$n for the function */
  3870.  
  3871.     SaveNumericParameters (wp, &s_Parameters);
  3872.  
  3873. /* Save Getopts pointers */
  3874.  
  3875.     GetGetoptsValues (&GetoptsSave);
  3876.  
  3877. /* Process the function */
  3878.  
  3879.     if (setjmp (BreakContinue.CurrentReturnPoint) == 0)
  3880.     {
  3881.     CurrentFunction = fop;
  3882.     Break_List = (Break_C *)NULL;
  3883.     BreakContinue.NextExitLevel = Return_List;
  3884.     Return_List = &BreakContinue;
  3885.     New = CopyFunction (fop->tree->left);
  3886.     *RetVal = ExecuteParseTree (New, NOPIPE, NOPIPE, EXEC_FUNCTION);
  3887.     }
  3888.  
  3889. /* A return has been executed - Unlike, while and for, we just need to
  3890.  * restore the local execute stack level and the return will restore
  3891.  * the correct I/O.
  3892.  */
  3893.  
  3894.     else
  3895.     *RetVal = (int)GetVariableAsNumeric (StatusVariable);
  3896.  
  3897.     if (!TrapZeroExists)
  3898.     RunTrapCommand (0);        /* Exit trap            */
  3899.  
  3900. /* Restore the old $0, and previous return address */
  3901.  
  3902.     SaveGetoptsValues (GetoptsSave.Index, GetoptsSave.SubIndex);
  3903.     Break_List  = s_BList;
  3904.     Return_List = s_RList;
  3905.     SShell_List = s_SList;
  3906.     CurrentFunction = s_CurrentFunction;
  3907.     RestoreTheParameters (&s_Parameters);
  3908.  
  3909.     return TRUE;
  3910. }
  3911.  
  3912. /*
  3913.  * Print Load error message
  3914.  */
  3915.  
  3916. #if (OS_TYPE == OS_OS2)
  3917. static void F_LOCAL PrintLoadError (char *path)
  3918. {
  3919.     if (*FailName)
  3920.     PrintWarningMessage ("%s: %s\nSYS1804: Cannot find file - %s", path,
  3921.                  ConvertErrorNumber(), FailName);
  3922.  
  3923.     else if (OS_DosExecPgmReturnCode)
  3924.     PrintWarningMessage ("%s: %s\n%s", path, ConvertErrorNumber(),
  3925.              GetOSSystemErrorMessage (OS_DosExecPgmReturnCode));
  3926.  
  3927.     else
  3928.     PrintWarningMessage ("%s: %s", path, ConvertErrorNumber());
  3929. }
  3930. #endif
  3931.  
  3932. /* NT Version */
  3933.  
  3934. #if (OS_TYPE == OS_NT)
  3935. static void F_LOCAL PrintLoadError (char *path)
  3936. {
  3937.     if (OS_DosExecPgmReturnCode)
  3938.     PrintWarningMessage ("%s: %s\n%s", path, ConvertErrorNumber(),
  3939.              GetOSSystemErrorMessage (OS_DosExecPgmReturnCode));
  3940.  
  3941.     else
  3942.     PrintWarningMessage ("%s: %s", path, ConvertErrorNumber());
  3943. }
  3944. #endif
  3945.  
  3946. /* DOS Version */
  3947.  
  3948. #if (OS_TYPE == OS_DOS)
  3949. static void F_LOCAL PrintLoadError (char *path)
  3950. {
  3951.     PrintWarningMessage (BasicErrorMessage, path, ConvertErrorNumber ());
  3952. }
  3953. #endif
  3954.  
  3955. /* DOS Version */
  3956.  
  3957. #if (OS_TYPE == OS_UNIX)
  3958. static void F_LOCAL PrintLoadError (char *path)
  3959. {
  3960.     PrintWarningMessage (BasicErrorMessage, path, ConvertErrorNumber ());
  3961. }
  3962. #endif
  3963.  
  3964. /*
  3965.  * Make the exported environment from the exported names in the dictionary.
  3966.  * Keyword assignments will already have been done.  Convert to MSDOS
  3967.  * format if flag set and m enabled
  3968.  */
  3969.  
  3970. static char ** F_LOCAL BuildCommandEnvironment (void)
  3971. {
  3972. /* Update SECONDS and RANDOM */
  3973.  
  3974.     HandleSECONDandRANDOM ();
  3975.  
  3976. /* Build the environment by walking the tree */
  3977.  
  3978.     BCE_WordList = (Word_B *)NULL;
  3979.     BCE_Length   = 0;
  3980.  
  3981.     twalk (VariableTree, BuildEnvironmentEntry);
  3982.  
  3983.     if (BCE_Length >= 0x7f00)
  3984.         return (char **)NULL;
  3985.  
  3986.     return GetWordList (AddWordToBlock (NOWORD, BCE_WordList));
  3987. }
  3988.  
  3989. /*
  3990.  * TWALK Function - Build Export VARIABLE list from VARIABLE tree
  3991.  */
  3992.  
  3993. static void BuildEnvironmentEntry (const void *key, VISIT visit, int level)
  3994. {
  3995.     VariableList    *vp = *(VariableList **)key;
  3996.     char        *cp;
  3997.     char        *sp;
  3998.     int            tlen;
  3999.  
  4000.     if ((visit != postorder) && (visit != leaf))
  4001.     return;
  4002.  
  4003.     if ((vp->status & STATUS_EXPORT) && (vp->index == 0))
  4004.     {
  4005.     cp = GetVariableAsString (vp->name, TRUE);
  4006.     tlen = strlen (vp->name) + strlen (cp) + 2;
  4007.  
  4008.     if ((BCE_Length += tlen) >= 0x7f00)
  4009.         return;
  4010.  
  4011.     strcpy ((sp = GetAllocatedSpace (tlen)), vp->name);
  4012.     SetMemoryAreaNumber ((void *)sp, MemoryAreaLevel);
  4013.     strcat (sp, "=");
  4014.     strcat (sp, cp);
  4015.  
  4016.     BCE_WordList = AddWordToBlock (sp, BCE_WordList);
  4017.  
  4018. /* If MSDOS mode, we need to copy the variable, convert / to \ and put
  4019.  * the copy in the environment list instead
  4020.  */
  4021.  
  4022.     if (((ShellGlobalFlags & FLAGS_MSDOS_FORMAT) ||
  4023.          (ExecProcessingMode.Flags & EP_EXPORT)) &&
  4024.         (vp->status & STATUS_CONVERT_MSDOS))
  4025.     {
  4026.         cp = StringCopy (BCE_WordList->w_words[BCE_WordList->w_nword - 1]);
  4027.         BCE_WordList->w_words[BCE_WordList->w_nword - 1] = PATH_TO_DOS (cp);
  4028.     }
  4029.     }
  4030. }
  4031.  
  4032. /*
  4033.  * Parse and Execute a command in the current shell
  4034.  */
  4035.  
  4036. int    RunACommand (Source *s, char **params)
  4037. {
  4038.     jmp_buf        erp;
  4039.     int            RetVal = -1;
  4040.     Break_C        *S_RList = Return_List;    /* Save loval links    */
  4041.     Break_C        *S_BList = Break_List;
  4042.     int            LS_depth = Execute_stack_depth++;
  4043.     C_Op        *outtree;
  4044.     bool        s_ProcessingEXECCommand = ProcessingEXECCommand;
  4045.     SaveParameters    s_Parameters;
  4046.  
  4047. /* Create a new save area */
  4048.  
  4049.     MemoryAreaLevel++;
  4050.  
  4051. /* Set up $0..$n for the command if appropriate.  Note that $0 does not
  4052.  * change
  4053.  */
  4054.  
  4055.     if (params != NOWORDS)
  4056.     {
  4057.     SaveNumericParameters (params, &s_Parameters);
  4058.     ParameterArray[0] = s_Parameters.Array[0];
  4059.     }
  4060.  
  4061. /* Execute the command */
  4062.  
  4063.     CreateNewEnvironment ();
  4064.  
  4065.     Return_List = (Break_C *)NULL;
  4066.     Break_List  = (Break_C *)NULL;
  4067.     ProcessingEXECCommand = TRUE;
  4068.     e.ErrorReturnPoint = (ErrorPoint)NULL;
  4069.  
  4070.     if (SetErrorPoint (erp) == 0)
  4071.     {
  4072.  
  4073. /* Read Input until completed */
  4074.  
  4075.     while (TRUE)
  4076.     {
  4077.         if (((outtree = BuildParseTree (s)) != (C_Op *)NULL) &&
  4078.             (outtree->type == TEOF))
  4079.         break;
  4080.  
  4081.         RetVal = ExecuteParseTree (outtree, NOPIPE, NOPIPE, 0);
  4082.     }
  4083.     }
  4084.  
  4085.     QuitCurrentEnvironment ();
  4086.  
  4087. /* Restore the environment */
  4088.  
  4089.     ClearExtendedLineFile ();
  4090.     Return_List = S_RList;
  4091.     Break_List = S_BList;
  4092.     ProcessingEXECCommand = s_ProcessingEXECCommand;
  4093.  
  4094. /* Restore $0..$n */
  4095.  
  4096.     if (params != NOWORDS)
  4097.     RestoreTheParameters (&s_Parameters);
  4098.  
  4099.     RestoreEnvironment (RetVal, LS_depth);
  4100.     ReleaseMemoryArea (MemoryAreaLevel--);
  4101.     return RetVal;
  4102. }
  4103.  
  4104. /*
  4105.  * Get the OS/2 Error message
  4106.  */
  4107.  
  4108. #if (OS_TYPE == OS_OS2) 
  4109. char    *GetOSSystemErrorMessage (OSCALL_RET code)
  4110. {
  4111.     static char        Buffer [FFNAME_MAX + 9];
  4112.     char        *Buffer1;
  4113.     OSCALL_PARAM    MsgLength;
  4114.     OSCALL_RET        rc;
  4115.     char        *ip;
  4116.     char        *op = Buffer;
  4117.     char        *sp;
  4118.     int            len;
  4119.  
  4120. /* For some reason DPATH does not work with the DosGetMessage API as the
  4121.  * spec say it should on OS/2 2.x.  Probably something we do.  It usually
  4122.  * is!  So emulate it!
  4123.  */
  4124.  
  4125.     if ((len = strlen (sp = GetVariableAsString ("DPATH", FALSE))) < FFNAME_MAX)
  4126.     len = FFNAME_MAX;
  4127.  
  4128.     if ((Buffer1 = GetAllocatedSpace (len)) == (char *)NULL)
  4129.     {
  4130.     sprintf (Buffer, "SYS%.4d: No memory to get message text", code);
  4131.     return Buffer;
  4132.     }
  4133.  
  4134.     sp = PATH_TO_UNIX (strcpy (Buffer1, sp));
  4135.  
  4136.     do
  4137.     {
  4138.     sp = BuildNextFullPathName (sp, "OSO001.MSG", Buffer);
  4139.     } while ((access (Buffer, F_OK) != 0) && (sp != (char *)NULL));
  4140.  
  4141. /* If not found - use the default */
  4142.  
  4143.     if (sp == (char *)NULL)
  4144.     {
  4145.     strcpy (Buffer, "c:/OS2/SYSTEM/OSO001.MSG");
  4146.     *Buffer = GetDriveLetter (GetRootDiskDrive ());
  4147.     }
  4148.  
  4149. /* Read the message */
  4150.  
  4151.     if ((rc = DosGetMessage (NULL, 0, ip = Buffer1, len, code, Buffer,
  4152.                     &MsgLength)))
  4153.     sprintf (Buffer, "SYS%.4d: No error message available (%d)",
  4154.          code, rc);
  4155.  
  4156.     else
  4157.     {
  4158.     if ((Buffer1[MsgLength - 1] == CHAR_NEW_LINE) &&
  4159.         (Buffer1[MsgLength - 2] == CHAR_RETURN))
  4160.         Buffer1[MsgLength - 2] = 0;
  4161.  
  4162.     else
  4163.         Buffer1[MsgLength] = 0;
  4164.  
  4165. /* Check the error number is there */
  4166.  
  4167.     if (strncmp (Buffer1, "SYS", 3) != 0)
  4168.     {
  4169.         sprintf (op, "SYS%.4d: ", code);
  4170.         op += strlen (op);
  4171.     }
  4172.  
  4173. /* Remove interior CRs & NLs */
  4174.  
  4175.     while ((*(op) = *(ip++)))
  4176.     {
  4177.         if (*op == CHAR_NEW_LINE)
  4178.         *(op++) = CHAR_SPACE;
  4179.  
  4180.         else if (*op != CHAR_RETURN)
  4181.         op++;
  4182.     }
  4183.     }
  4184.  
  4185.     ReleaseMemoryCell (Buffer1);
  4186.     return Buffer;
  4187. }
  4188.  
  4189. #  ifdef __WATCOMC__
  4190.  
  4191. /*
  4192.  * A cheat for WATCOM which does not have the DosGetMessage API.  This
  4193.  * function is based an interpretation of the System message file and one or
  4194.  * two others.  The format of the header is not know except for the flag at
  4195.  * offset 0x0f.  It is not guaranteed to work.
  4196.  */
  4197.  
  4198. APIRET APIENTRY    DosGetMessage  (PCHAR *ppchVTable,
  4199.                 ULONG usVCount,
  4200.                 PCHAR pchBuf,
  4201.                 ULONG cbBuf,
  4202.                 ULONG usMsgNum,
  4203.                 PSZ pszFileName,
  4204.                 PULONG pcbMsg)
  4205. {
  4206.     int                fd;
  4207. #  pragma pack (1)
  4208.     union {
  4209.     struct {
  4210.         USHORT        Start;
  4211.         USHORT        End;
  4212.     }            ShortE;
  4213.     struct {
  4214.         ULONG        Start;
  4215.         ULONG         End;
  4216.     }            LongE;
  4217.     }                Start, Current;
  4218. #  pragma pack ()
  4219.     char            Type;
  4220.     int                Len;
  4221.     unsigned long        Offset;
  4222.     APIRET            Res;
  4223.  
  4224.     if ((fd = open (pszFileName, O_RDONLY | O_BINARY)) < 0)
  4225.     return _doserrno;
  4226.  
  4227. /* Get the message file format */
  4228.  
  4229.     if ((lseek (fd, 0x0fL, SEEK_SET) != 0x0fL) ||
  4230.     (read (fd, &Type, 1) != 1) ||
  4231.     (lseek (fd, 0x1fL, SEEK_SET) != 0x1fL))
  4232.     {
  4233.     Res = _doserrno;
  4234.     close (fd);
  4235.     return Res;
  4236.     }
  4237.  
  4238. /* Read the start of message text location */
  4239.  
  4240.     Len = (Type) ? 4 : 8;
  4241.  
  4242.     if (read (fd, &Start, Len) != Len)
  4243.     {
  4244.     Res = _doserrno;
  4245.     close (fd);
  4246.     return Res;
  4247.     }
  4248.  
  4249. /* Check the offset to the message */
  4250.  
  4251.     Offset = 0x1fL + (usMsgNum * ((Type) ? 2L : 4L));
  4252.  
  4253.     if (((Type) && (Offset >= Start.ShortE.Start)) ||
  4254.     ((!Type) && (Offset >= Start.LongE.Start)))
  4255.     {
  4256.     close (fd);
  4257.     return ERROR_MR_MID_NOT_FOUND;
  4258.     }
  4259.  
  4260. /* Get the message location */
  4261.  
  4262.     if ((lseek (fd, Offset, SEEK_SET) != Offset) ||
  4263.     (read (fd, &Current, Len) != Len))
  4264.     {
  4265.     Res = _doserrno;
  4266.     close (fd);
  4267.     return Res;
  4268.     }
  4269.  
  4270.     if (((Type) && (Offset == Start.ShortE.Start - 2)) ||
  4271.     ((!Type) && (Offset == Start.LongE.Start - 4)))
  4272.     {
  4273.     if ((Offset = lseek (fd, 0L, SEEK_END)) == -1L)
  4274.     {
  4275.         Res = _doserrno;
  4276.         close (fd);
  4277.         return Res;
  4278.     }
  4279.  
  4280.     else if (Type)
  4281.         Current.ShortE.End = (USHORT)Offset;
  4282.  
  4283.     else
  4284.         Current.LongE.End = Offset;
  4285.     }
  4286.  
  4287. /* Get the message length */
  4288.  
  4289.     if (Type)
  4290.     {
  4291.     *pcbMsg = Current.ShortE.End - Current.ShortE.Start;
  4292.     Offset = Current.ShortE.Start;
  4293.     }
  4294.  
  4295.     else
  4296.     {
  4297.     *pcbMsg = (USHORT)(Current.LongE.End - Current.LongE.Start);
  4298.     Offset = Current.LongE.Start;
  4299.     }
  4300.    
  4301. /* Check the message length */
  4302.  
  4303.     *pcbMsg += 8;
  4304.  
  4305.     if (*pcbMsg >= cbBuf)
  4306.     return ERROR_MR_MSG_TOO_LONG;
  4307.     
  4308.     sprintf (pchBuf, "SYS%.4d: ", usMsgNum);
  4309.  
  4310. /* Seek to the start of the message and read its type */
  4311.  
  4312.     if ((lseek (fd, Offset, SEEK_SET) != Offset) ||
  4313.     (read (fd, &Type, 1) != 1))
  4314.     {
  4315.     Res = _doserrno;
  4316.     close (fd);
  4317.     return Res;
  4318.     }
  4319.     
  4320.     if (Type == '?') 
  4321.     {
  4322.     close (fd);
  4323.     return ERROR_MR_MID_NOT_FOUND;
  4324.     }
  4325.  
  4326. /* Get the message itself */
  4327.  
  4328.     else if (read (fd, pchBuf + 9, *pcbMsg - 8) != *pcbMsg - 8)
  4329.     {
  4330.     Res = _doserrno;
  4331.     close (fd);
  4332.     return Res;
  4333.     }
  4334.  
  4335.     close (fd);
  4336.     *(pchBuf + *pcbMsg) = 0;
  4337.     return 0;
  4338. }
  4339. #  endif
  4340. #endif
  4341.  
  4342. /*
  4343.  * Get the Win NT Error message
  4344.  */
  4345.  
  4346. #if (OS_TYPE == OS_NT) 
  4347. char    *GetOSSystemErrorMessage (OSCALL_RET code)
  4348. {
  4349.     DWORD        Source = 0;
  4350.     static char        EBuffer[100];
  4351.     char        *OSBuffer;
  4352.     char        *Buffer1;
  4353.     char        *ip;
  4354.     char        *op;
  4355.  
  4356. /* Read the message */
  4357.  
  4358.     if (!FormatMessage ((FORMAT_MESSAGE_FROM_SYSTEM |
  4359.                  FORMAT_MESSAGE_ALLOCATE_BUFFER), &Source, code, 0,
  4360.                 (LPSTR)&OSBuffer, 100, NULL))
  4361.     {
  4362.     sprintf (EBuffer, "SYS%.4d: No error message available (%ld)",
  4363.          code, GetLastError ());
  4364.     OSBuffer = EBuffer;
  4365.     }
  4366.  
  4367.     else
  4368.     {
  4369.     op = &OSBuffer[strlen (OSBuffer) - 1];
  4370.  
  4371.     while (isspace (*op) && (op != OSBuffer))
  4372.         op--;
  4373.     }
  4374.  
  4375. /* Allocate local space.  If there isn't any give up and hope for the best */
  4376.  
  4377.     if ((Buffer1 = GetAllocatedSpace (strlen (OSBuffer) + 20)) == (char *)NULL)
  4378.     return OSBuffer;
  4379.  
  4380. /* Transfer the system buffer to a local buffer.
  4381.  * Check the error number is there
  4382.  */
  4383.  
  4384.     op = Buffer1;
  4385.     ip = OSBuffer;
  4386.  
  4387.     if (strncmp (OSBuffer, "SYS", 3) != 0)
  4388.     {
  4389.     sprintf (op, "SYS%.4d: ", code);
  4390.     op += strlen (op);
  4391.     }
  4392.  
  4393. /* Remove interior CRs & NLs */
  4394.  
  4395.     while (*(op) = *(ip++))
  4396.     {
  4397.     if (*op == CHAR_NEW_LINE)
  4398.         *(op++) = CHAR_SPACE;
  4399.  
  4400.     else if (*op != CHAR_RETURN)
  4401.         op++;
  4402.     }
  4403.  
  4404.     if (OSBuffer != EBuffer)
  4405.     LocalFree (OSBuffer);
  4406.  
  4407.     return Buffer1;
  4408. }
  4409. #endif
  4410.  
  4411. /*
  4412.  * Display a started job info
  4413.  */
  4414.  
  4415. #if (OS_TYPE != OS_DOS)
  4416. static void F_LOCAL PrintPidStarted (void)
  4417. {
  4418.     if (PidInfo.Valid)
  4419.     fprintf (stderr, "[%d] %d\n", PidInfo.JobNo, PidInfo.PidNo);
  4420.  
  4421.     PidInfo.Valid = FALSE;
  4422. }
  4423. #endif
  4424.  
  4425. /*
  4426.  * Print a list of names, with or without numbers
  4427.  */
  4428.  
  4429. void    PrintAList (int ArgCount, char **ArgList)
  4430. {
  4431.     char    **pp = ArgList;
  4432.     int        i, j;
  4433.     int        ix;
  4434.     int        MaxArgWidth = 0;
  4435.     int        NumberWidth = 0;
  4436.     int        ncols;
  4437.     int        nrows;
  4438.  
  4439.     if (!ArgCount)
  4440.         return;
  4441.  
  4442. /* get dimensions of the list */
  4443.  
  4444.     while (*pp != (char *)NULL)
  4445.     {
  4446.     i = strlen (*(pp++)) + 1;
  4447.     MaxArgWidth = (i > MaxArgWidth) ? i : MaxArgWidth;
  4448.     }
  4449.  
  4450. /*
  4451.  * We print an index of the form
  4452.  *    %d)
  4453.  * in front of each entry.  Get the max width of this
  4454.  */
  4455.  
  4456.     for (i = ArgCount, NumberWidth = 1; i >= 10; i /= 10)
  4457.     NumberWidth++;
  4458.  
  4459. /* In the case of numbered lists, we go down if less than screen length */
  4460.  
  4461.     if (ArgCount < (MaximumLines - 5))
  4462.     {
  4463.     ncols = 1;
  4464.     nrows = ArgCount;
  4465.     }
  4466.  
  4467.     else
  4468.     {
  4469.     ncols = MaximumColumns / (MaxArgWidth + NumberWidth + 3);
  4470.     nrows = ArgCount / ncols;
  4471.  
  4472.     if (ArgCount % ncols)
  4473.         nrows++;
  4474.  
  4475.     if (!nrows)
  4476.         nrows = 1;
  4477.  
  4478.     if (ncols > nrows)
  4479.     {
  4480.         nrows = ncols;
  4481.         ncols = 1;
  4482.     }
  4483.     }
  4484.  
  4485. /* Display the list */
  4486.  
  4487.     for (i = 0; i < nrows; i++)
  4488.     {
  4489.     for (j = 0; j < ncols; j++)
  4490.     {
  4491.         if ((ix = j * nrows + i) < ArgCount)
  4492.         {
  4493.         printf ("%*d) ", NumberWidth, ix + 1);
  4494.  
  4495.         if (j != (ncols - 1))
  4496.             printf ("%-*.*s", MaxArgWidth, MaxArgWidth, ArgList[ix]);
  4497.  
  4498.         else
  4499.             printf ("%-s", ArgList[ix]);
  4500.         }
  4501.     }
  4502.  
  4503.     fputchar (CHAR_NEW_LINE);
  4504.     }
  4505. }
  4506.  
  4507. /* 
  4508.  * Are we tracking all commands?  Check there is no path in the command
  4509.  */
  4510.  
  4511. static void F_LOCAL TrackAllCommands (char *path, char *arg)
  4512. {
  4513.     if (FL_TEST (FLAG_TRACK_ALL) &&
  4514.     (FindPathCharacter (arg) == (char *)NULL) &&
  4515.     (!IsDriveCharacter (arg[1])))
  4516.     SaveAlias (arg, PATH_TO_UNIX (path), TRUE);
  4517. }
  4518.  
  4519.  
  4520. /*
  4521.  * Get the application type and get we can do it
  4522.  */
  4523.  
  4524. static bool F_LOCAL GetApplicationType (char *path)
  4525. {
  4526.     ApplicationType = QueryApplicationType (path);
  4527.  
  4528. /* Some type of error */
  4529.  
  4530.     if (ApplicationType & EXETYPE_ERROR)
  4531.     {
  4532.     if (ApplicationType == EXETYPE_UNKNOWN)
  4533.     {
  4534.         if (!FL_TEST (FLAG_WARNING))
  4535.         fprintf (stderr, "sh: Cannot determine executable type <%s>\n",
  4536.              path);
  4537.  
  4538.         return TRUE;
  4539.     }
  4540.     
  4541.     else if (ApplicationType == EXETYPE_BAD_FILE)
  4542.         errno = ENOENT;
  4543.  
  4544.     else
  4545.         errno = ENOEXEC;
  4546.  
  4547.     return FALSE;
  4548.     }
  4549.  
  4550. /*
  4551.  * This is where it gets complicated - Sort out DOS!
  4552.  */
  4553.  
  4554. #if (OS_TYPE == OS_DOS)
  4555.     if (ExecProcessingMode.Flags & EP_IGNTYPE)
  4556.     {
  4557.     ApplicationType = EXETYPE_DOS_CUI;
  4558.     return TRUE;
  4559.     }
  4560.  
  4561.     else if (ApplicationType == EXETYPE_DOS_GUI)
  4562.     {
  4563.     if ((BaseOS == BASE_OS_WIN) &&
  4564.         (GetVariableAsString (LIT_STARTWINP, FALSE) == null))
  4565.     {
  4566.         if (!FL_TEST (FLAG_WARNING))
  4567.         feputs ("sh: Start this applications from Windows\n");
  4568.  
  4569.         errno = ENOEXEC;
  4570.         return FALSE;
  4571.     }
  4572.     }
  4573.  
  4574.     else if ((ApplicationType == EXETYPE_DOS_32) && (BaseOS == BASE_OS_NT))
  4575.     return BadApplication ("DOS 32-bit");
  4576.  
  4577.     else if (ApplicationType & EXETYPE_OS2)
  4578.     return BadApplication ("OS/2");
  4579.  
  4580.     else if (ApplicationType & EXETYPE_NT)
  4581.     return (BaseOS == BASE_OS_NT) ? TRUE : BadApplication ("Win NT");
  4582.  
  4583. #elif (OS_TYPE == OS_OS2)
  4584.  
  4585. /*
  4586.  * OK - now OS/2
  4587.  */
  4588.  
  4589.     if (ApplicationType & EXETYPE_NT)
  4590.     return BadApplication ("Win NT");
  4591.  
  4592. #  if (OS_SIZE == OS_16)
  4593.     if (OS_VERS_N < 2)
  4594.     {
  4595.     if (ApplicationType & EXETYPE_DOS)
  4596.         return BadApplication (LIT_dos);
  4597.  
  4598.     else if (ApplicationType & EXETYPE_OS2_32) 
  4599.         return BadApplication ("OS/2 32-bit");
  4600.     }
  4601. #  endif
  4602.  
  4603.     if (ApplicationType == EXETYPE_DOS_32)
  4604.     return BadApplication ("DOS 32-bit");
  4605.  
  4606. #elif (OS_TYPE == OS_NT)
  4607.  
  4608. /*
  4609.  * OK - now NT
  4610.  */
  4611.  
  4612.     if (ApplicationType == EXETYPE_DOS_32)
  4613.     return BadApplication ("DOS 32-bit");
  4614.  
  4615.     else if (ApplicationType & EXETYPE_OS2_32) 
  4616.     return BadApplication ("OS/2 32-bit");
  4617. #endif
  4618.  
  4619. /* In the end - execute it */
  4620.  
  4621.     return TRUE;
  4622. }
  4623.  
  4624. /*
  4625.  * Print Bad application warning
  4626.  */
  4627.  
  4628. static bool F_LOCAL BadApplication (char *mes)
  4629. {
  4630.     if (!FL_TEST (FLAG_WARNING))
  4631.     fprintf (stderr, "sh: Cannot execute %s applications\n", mes);
  4632.  
  4633.     errno = ENOEXEC;
  4634.     return FALSE;
  4635. }
  4636.  
  4637. /*
  4638.  * Handle a Windows Program
  4639.  */
  4640.  
  4641. #if (OS_TYPE != OS_UNIX)
  4642. static int F_LOCAL ExecuteWindows (char *Fullpath, char **argv, char **envp,
  4643.                    int  ForkAction)
  4644. {
  4645.     int        res;
  4646. #  if (OS_TYPE == OS_DOS)
  4647.     Word_B    *wb = (Word_B *)NULL;
  4648.  
  4649.     if (!SetUpCLI ((BaseOS != BASE_OS_WIN) 
  4650.             ? "win"
  4651.             : GetVariableAsString (LIT_STARTWINP, FALSE), &wb))
  4652.     return -1;
  4653.  
  4654. #  elif (OS_TYPE == OS_OS2)
  4655.     Word_B    *wb = AddWordToBlock ("winos2", (Word_B *)NULL);
  4656. #  elif (OS_TYPE == OS_NT)
  4657.     Word_B    *wb = (Word_B *)NULL;
  4658. #  endif
  4659.  
  4660. /* Add the rest of the parameters and execute */
  4661.  
  4662.     ForkAction |= EXEC_WINDOWS;
  4663.  
  4664.     res = ExecuteSpecialProcessor (Fullpath, argv, envp, ForkAction, wb);
  4665.  
  4666. /* A cheat to stop us exec'ing the program again.  Normally,
  4667.  * ExecuteSpecialProcessor is call below ExecuteProgram.  However, in the
  4668.  * case of a Windows prog, its called above, so we set this to stop
  4669.  * Executeprogram invoking the program again, but telling it that no
  4670.  * swapping was required.
  4671.  */
  4672.  
  4673.     ExecProcessingMode.Flags = EP_NOSWAP;
  4674.     return res;
  4675. }
  4676. #endif
  4677.  
  4678. /*
  4679.  * Generic processing for script and windows programs
  4680.  */
  4681.  
  4682. static int F_LOCAL ExecuteSpecialProcessor (char *Fullpath, char **argv,
  4683.                         char **envp, int ForkAction,
  4684.                         Word_B *wb)
  4685. {
  4686.     char    **nargv;
  4687.     char    *p_name;        /* Program name        */
  4688.     char    *cp;
  4689.     int        j;
  4690.  
  4691. /* Add the rest of the parameters */
  4692.  
  4693.     wb = AddWordToBlock (Fullpath, wb);
  4694.  
  4695.     j = 1;
  4696.     while (argv[j] != NOWORD)
  4697.     wb = AddWordToBlock (argv[j++], wb);
  4698.  
  4699. /* Execute the program */
  4700.  
  4701.     nargv = GetWordList (AddWordToBlock (NOWORD, wb));
  4702.  
  4703. /* Special for UNIX compatability, use ourselves */
  4704.  
  4705.     if ((strcmp (nargv[0], "/bin/sh") == 0) ||
  4706.     (strcmp (nargv[0], "/bin/ksh") == 0))
  4707.     nargv[0] = GetVariableAsString (ShellVariableName, FALSE);
  4708.  
  4709. /* See if the program exists.  If it doesn't, strip the path */
  4710.  
  4711.     else if (((cp = strrchr (nargv[0], CHAR_UNIX_DIRECTORY)) != (char *)NULL) &&
  4712.          ((p_name = AllocateMemoryCell (FFNAME_MAX)) != (char *)NULL))
  4713.     {
  4714.     if (FindLocationOfExecutable (p_name, nargv[0]) == EXTENSION_NOT_FOUND)
  4715.         strcpy (nargv[0], cp + 1);
  4716.  
  4717.     ReleaseMemoryCell (p_name);
  4718.     }
  4719.  
  4720. /* Get the new program mode */
  4721.  
  4722.     CheckProgramMode (*nargv, &ExecProcessingMode);
  4723.  
  4724.     j = EnvironExecute (nargv, ForkAction);
  4725.  
  4726.     if (ExecProcessingMode.Flags != EP_ENVIRON)
  4727.     j = LocalExecve (nargv, envp, ForkAction);
  4728.  
  4729. /* 0 is a special case - see ConvertErrorNumber */
  4730.  
  4731.     if (j == -1)
  4732.     errno = 0;
  4733.  
  4734.     return j;
  4735. }
  4736.  
  4737. /*
  4738.  * Parse a new CLI string
  4739.  */
  4740.  
  4741. static bool F_LOCAL SetUpCLI (char *string, Word_B **wb)
  4742. {
  4743.     char    *sp = StringCopy (string);
  4744.  
  4745.     if (sp == null)
  4746.     return FALSE;
  4747.  
  4748.     *wb = SplitString (sp, *wb);
  4749.     return TRUE;
  4750. }
  4751.